diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | 0001-daemon-Allow-SystemAccount-false-to-be-set-in-cache-.patch | 304 | ||||
| -rw-r--r-- | 0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch | 1839 | ||||
| -rw-r--r-- | 0001-data-don-t-send-change-updates-for-login-history.patch | 88 | ||||
| -rw-r--r-- | 0001-lib-save-os-when-creating-user.patch | 894 | ||||
| -rw-r--r-- | 0001-user-Introduce-user-templates-for-setting-default-se.patch | 925 | ||||
| -rw-r--r-- | 0002-main-Allow-cache-files-to-be-marked-immutable.patch | 195 | ||||
| -rw-r--r-- | accountsservice.spec | 412 | ||||
| -rw-r--r-- | sources | 1 | ||||
| -rw-r--r-- | user-template | 13 | 
10 files changed, 4672 insertions, 0 deletions
@@ -0,0 +1 @@ +/accountsservice-0.6.55.tar.xz diff --git a/0001-daemon-Allow-SystemAccount-false-to-be-set-in-cache-.patch b/0001-daemon-Allow-SystemAccount-false-to-be-set-in-cache-.patch new file mode 100644 index 0000000..84d5265 --- /dev/null +++ b/0001-daemon-Allow-SystemAccount-false-to-be-set-in-cache-.patch @@ -0,0 +1,304 @@ +From 14c902f42a4ea74ce9450eb53817e1bf5be05d26 Mon Sep 17 00:00:00 2001 +From: Ray Strode <rstrode@redhat.com> +Date: Wed, 8 Sep 2021 16:38:17 -0400 +Subject: [PATCH 1/2] daemon: Allow SystemAccount=false to be set in cache file + +At the moment we do dodgy checks based on uid to decide whether or not +an account is a system account. + +For legacy reasons, sometimes normal users have really low UIDs. + +This commit reshuffles things, so the cache file "wins" for deciding +whether or not a user is a system user. +--- + src/daemon.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +diff --git a/src/daemon.c b/src/daemon.c +index 66ac7ba..2b6650b 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -219,60 +219,68 @@ entry_generator_fgetpwent (Daemon       *daemon, +                 if (g_hash_table_size (shadow_users) == 0) { +                         g_clear_pointer (&shadow_users, g_hash_table_unref); +                         return NULL; +                 } +  +                 fp = fopen (PATH_PASSWD, "r"); +                 if (fp == NULL) { +                         g_clear_pointer (&shadow_users, g_hash_table_unref); +                         g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno)); +                         return NULL; +                 } +  +                 generator_state = g_malloc0 (sizeof (*generator_state)); +                 generator_state->fp = fp; +                 generator_state->users = shadow_users; +  +                 *state = generator_state; +         } +  +         /* Every iteration */ +         generator_state = *state; +  +         if (g_hash_table_size (users) < MAX_LOCAL_USERS) { +                 pwent = fgetpwent (generator_state->fp); +                 if (pwent != NULL) { +                         shadow_entry_buffers = g_hash_table_lookup (generator_state->users, pwent->pw_name); +  +                         if (shadow_entry_buffers != NULL) { +                             *spent = &shadow_entry_buffers->spbuf; +                         } ++ ++                        /* Skip system users... */ ++                        if (!user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, (*spent)? (*spent)->sp_pwdp : NULL)) { ++                                g_debug ("skipping user: %s", pwent->pw_name); ++ ++                                return entry_generator_fgetpwent (daemon, users, state, spent); ++                        } ++ +                         return pwent; +                 } +         } +  +         /* Last iteration */ +         fclose (generator_state->fp); +         g_hash_table_unref (generator_state->users); +         g_free (generator_state); +         *state = NULL; +  +         return NULL; + } +  + static struct passwd * + entry_generator_cachedir (Daemon       *daemon, +                           GHashTable   *users, +                           gpointer     *state, +                           struct spwd **shadow_entry) + { +         struct passwd *pwent; +         g_autoptr(GError) error = NULL; +         gboolean regular; +         GHashTableIter iter; +         gpointer key, value; +         GDir *dir; +  +         /* First iteration */ +         if (*state == NULL) { +                 *state = g_dir_open (USERDIR, 0, &error); +                 if (error != NULL) { +@@ -373,66 +381,60 @@ entry_generator_requested_users (Daemon       *daemon, +                         } +                 } +         } +  +         /* Last iteration */ +  +         *state = NULL; +         return NULL; + } +  + static void + load_entries (Daemon             *daemon, +               GHashTable         *users, +               gboolean            explicitly_requested, +               EntryGeneratorFunc  entry_generator) + { +         DaemonPrivate *priv = daemon_get_instance_private (daemon); +         gpointer generator_state = NULL; +         struct passwd *pwent; +         struct spwd *spent = NULL; +         User *user = NULL; +  +         g_assert (entry_generator != NULL); +  +         for (;;) { +                 spent = NULL; +                 pwent = entry_generator (daemon, users, &generator_state, &spent); +                 if (pwent == NULL) +                         break; +  +-                /* Skip system users... */ +-                if (!explicitly_requested && !user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, spent? spent->sp_pwdp : NULL)) { +-                        g_debug ("skipping user: %s", pwent->pw_name); +-                        continue; +-                } +- +                 /* Only process users that haven't been processed yet. +                  * We do always make sure entries get promoted +                  * to "cached" status if they are supposed to be +                  */ +  +                 user = g_hash_table_lookup (users, pwent->pw_name); +  +                 if (user == NULL) { +                         user = g_hash_table_lookup (priv->users, pwent->pw_name); +                         if (user == NULL) { +                                 user = user_new (daemon, pwent->pw_uid); +                         } else { +                                 g_object_ref (user); +                         } +  +                         /* freeze & update users not already in the new list */ +                         g_object_freeze_notify (G_OBJECT (user)); +                         user_update_from_pwent (user, pwent, spent); +  +                         g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user); +                         g_debug ("loaded user: %s", user_get_user_name (user)); +                 } +  +                 if (!explicitly_requested) { +                         user_set_cached (user, TRUE); +                 } +         } +  +         /* Generator should have cleaned up */ +         g_assert (generator_state == NULL); +@@ -501,66 +503,66 @@ has_network_realms (Daemon *daemon) +  + static void + reload_users (Daemon *daemon) + { +         DaemonPrivate *priv = daemon_get_instance_private (daemon); +         AccountsAccounts *accounts = ACCOUNTS_ACCOUNTS (daemon); +         gboolean had_no_users, has_no_users, had_multiple_users, has_multiple_users; +         GHashTable *users; +         GHashTable *old_users; +         GHashTable *local; +         GHashTableIter iter; +         gsize number_of_normal_users = 0; +         gpointer name, value; +  +         /* Track the users that we saw during our (re)load */ +         users = create_users_hash_table (); +  +         /* +          * NOTE: As we load data from all the sources, notifies are +          * frozen in load_entries() and then thawed as we process +          * them below. +          */ +  +         /* Load the local users into our hash table */ +         load_entries (daemon, users, FALSE, entry_generator_fgetpwent); +         local = g_hash_table_new (g_str_hash, g_str_equal); +         g_hash_table_iter_init (&iter, users); +         while (g_hash_table_iter_next (&iter, &name, NULL)) +                 g_hash_table_add (local, name); +  +-        /* and add users to hash table that were explicitly requested  */ +-        load_entries (daemon, users, TRUE, entry_generator_requested_users); +- +         /* Now add/update users from other sources, possibly non-local */ +         load_entries (daemon, users, FALSE, entry_generator_cachedir); +  ++        /* and add users to hash table that were explicitly requested  */ ++        load_entries (daemon, users, TRUE, entry_generator_requested_users); ++ +         wtmp_helper_update_login_frequencies (users); +  +         /* Count the non-system users. Mark which users are local, which are not. */ +         g_hash_table_iter_init (&iter, users); +         while (g_hash_table_iter_next (&iter, &name, &value)) { +                 User *user = value; +                 if (!user_get_system_account (user)) +                         number_of_normal_users++; +                 user_update_local_account_property (user, g_hash_table_lookup (local, name) != NULL); +         } +         g_hash_table_destroy (local); +  +         had_no_users = accounts_accounts_get_has_no_users (accounts); +         has_no_users = number_of_normal_users == 0; +  +         if (has_no_users && has_network_realms (daemon)) { +                 g_debug ("No local users, but network realms detected, presuming there are remote users"); +                 has_no_users = FALSE; +         } +  +         if (had_no_users != has_no_users) +                 accounts_accounts_set_has_no_users (accounts, has_no_users); +  +         had_multiple_users = accounts_accounts_get_has_multiple_users (accounts); +         has_multiple_users = number_of_normal_users > 1; +  +         if (had_multiple_users != has_multiple_users) +                 accounts_accounts_set_has_multiple_users (accounts, has_multiple_users); +  +         /* Swap out the users */ +@@ -1017,73 +1019,71 @@ daemon_find_user_by_name (AccountsAccounts      *accounts, +  + static ListUserData * + list_user_data_new (Daemon                *daemon, +                     GDBusMethodInvocation *context) + { +         ListUserData *data; +  +         data = g_new0 (ListUserData, 1); +  +         data->daemon = g_object_ref (daemon); +         data->context = context; +  +         return data; + } +  + static void + list_user_data_free (ListUserData *data) + { +         g_object_unref (data->daemon); +         g_free (data); + } +  + static void + finish_list_cached_users (ListUserData *data) + { +         DaemonPrivate *priv = daemon_get_instance_private (data->daemon); +         g_autoptr(GPtrArray) object_paths = NULL; +         GHashTableIter iter; +         gpointer key, value; +         uid_t uid; +-        const gchar *shell; +  +         object_paths = g_ptr_array_new (); +  +         g_hash_table_iter_init (&iter, priv->users); +         while (g_hash_table_iter_next (&iter, &key, &value)) { +                 const gchar *name = key; +                 User *user = value; +  +                 uid = user_get_uid (user); +-                shell = user_get_shell (user); +  +-                if (!user_classify_is_human (uid, name, shell, NULL)) { ++                if (user_get_system_account (user)) { +                         g_debug ("user %s %ld excluded", name, (long) uid); +                         continue; +                 } +  +                 if (!user_get_cached (user)) { +                         g_debug ("user %s %ld not cached", name, (long) uid); +                         continue; +                 } +  +                 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); +  +         accounts_accounts_complete_list_cached_users (NULL, data->context, (const gchar * const *) object_paths->pdata); +  +         list_user_data_free (data); + } +  + static gboolean + daemon_list_cached_users (AccountsAccounts      *accounts, +                           GDBusMethodInvocation *context) + { +         Daemon *daemon = (Daemon*)accounts; +         DaemonPrivate *priv = daemon_get_instance_private (daemon); +         ListUserData *data; +  +         data = list_user_data_new (daemon, context); +  +         if (priv->reload_id > 0) { +--  +2.31.1 + diff --git a/0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch b/0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch new file mode 100644 index 0000000..5185345 --- /dev/null +++ b/0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch @@ -0,0 +1,1839 @@ +From 54b207649979475ea7f1fa5eaaea94be31d20935 Mon Sep 17 00:00:00 2001 +From: Ray Strode <rstrode@redhat.com> +Date: Fri, 13 Dec 2019 15:16:06 -0500 +Subject: [PATCH] daemon: if no local users, check if machine is enrolled in + network + +GDM will show gnome initial-setup if a machine has no local users. +But it's totally possible that a machine has only remote users, +and shouldn't have a local user. + +This commit detects that case, and avoids setting the HasNoUsers +property. +--- + data/org.freedesktop.realmd.xml | 730 ++++++++++++++++++++++++++++++++ + src/daemon.c                    |  63 ++- + src/meson.build                 |   1 + + src/org.freedesktop.realmd.xml  | 730 ++++++++++++++++++++++++++++++++ + 4 files changed, 1520 insertions(+), 4 deletions(-) + create mode 100644 data/org.freedesktop.realmd.xml + create mode 100644 src/org.freedesktop.realmd.xml + +diff --git a/data/org.freedesktop.realmd.xml b/data/org.freedesktop.realmd.xml +new file mode 100644 +index 0000000..c34a47a +--- /dev/null ++++ b/data/org.freedesktop.realmd.xml +@@ -0,0 +1,730 @@ ++<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" ++ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> ++<node name="/"> ++ ++	<!-- ++	  org.freedesktop.realmd.Provider: ++	  @short_description: a realm provider ++ ++	  Various realm providers represent different software implementations ++	  that provide access to realms or domains. ++ ++	  This interface is implemented by individual providers, but is ++	  aggregated globally at the system bus name ++	  <literal>org.freedesktop.realmd</literal> ++	  with the object path <literal>/org/freedesktop/realmd</literal> ++	--> ++	<interface name="org.freedesktop.realmd.Provider"> ++ ++		<!-- ++		  Name: the name of the provider ++ ++		  The name of the provider. This is not normally displayed ++		  to the user, but may be useful for diagnostics or debugging. ++		--> ++		<property name="Name" type="s" access="read"/> ++ ++		<!-- ++		  Version: the version of the provider ++ ++		  The version of the provider. This is not normally used in ++		  logic, but may be useful for diagnostics or debugging. ++		--> ++		<property name="Version" type="s" access="read"/> ++ ++		<!-- ++		  Realms: a list of realms ++ ++		  A list of known, enrolled or discovered realms. All realms ++		  that this provider knows about are listed here. As realms ++		  are discovered they are added to this list. ++ ++		  Each realm is represented by the DBus object path of the ++		  realm object. ++		--> ++		<property name="Realms" type="ao" access="read"/> ++ ++		<!-- ++		  Discover: ++		  @string: an input string to discover realms for ++		  @options: options for the discovery operation ++		  @relevance: the relevance of the returned results ++		  @realm: a list of realms discovered ++ ++		  Discover realms for the given string. The input @string is ++		  usually a domain or realm name, perhaps typed by a user. If ++		  an empty string is provided, the realm provider should try to ++		  discover a default realm, if possible (e.g. from DHCP). ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		    <listitem><para><literal>client-software</literal>: a string ++		      containing the client software identifier that the returned ++		      realms should match.</para></listitem> ++		    <listitem><para><literal>server-software</literal>: a string ++		      containing the client software identifier that the returned ++		      realms should match.</para></listitem> ++		    <listitem><para><literal>membership-software</literal>: a string ++		      containing the membership software identifier that the returned ++		      realms should match.</para></listitem> ++		  </itemizedlist> ++ ++		  The @relevance returned can be used to rank results from ++		  different discover calls to different providers. Implementors ++		  should return a positive number if the provider highly ++		  recommends that the realms be handled by this provider, ++		  or a zero if it can possibly handle the realms. Negative numbers ++		  should be returned if no realms are found. ++ ++		  This method does not return an error when no realms are ++		  discovered. It simply returns an empty @realm list. ++ ++		  To see diagnostic information about the discovery process, ++		  connect to the org.freedesktop.realmd.Service::Diagnostics ++		  signal. ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.discover-realm</literal>. ++ ++		  In addition to common DBus error results, this method may ++		  return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the discovery could not be run for some reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to perform a discovery ++		      operation.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Discover"> ++			<arg name="string" type="s" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++			<arg name="relevance" type="i" direction="out"/> ++			<arg name="realm" type="ao" direction="out"/> ++		</method> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.Service: ++	  @short_description: the realmd service ++ ++	  Global calls for managing the realmd service. Usually you'll want ++	  to use #org.freedesktop.realmd.Provider instead. ++ ++	  This interface is implemented by the realmd service, and is always ++	  available at the object path <literal>/org/freedesktop/realmd</literal> ++ ++	  The service also implements the ++	  <literal>org.freedesktop.DBus.ObjectManager</literal> interface which ++	  makes it easy to retrieve all realmd objects and properties in one go. ++	--> ++	<interface name="org.freedesktop.realmd.Service"> ++ ++		<!-- ++		  Cancel: ++		  @operation: the operation to cancel ++ ++		  Cancel a realmd operation. To be able to cancel an operation, ++		  pass a uniquely chosen <literal>operation</literal> string ++		  identifier as an option in the method's <literal>options</literal> ++		  argument. ++ ++		  These operation string identifiers should be unique per client ++		  calling the realmd service. ++ ++		  It is not guaranteed that the service can or will cancel the ++		  operation. For example, the operation may have already completed ++		  by the time this method is handled. The caller of the operation ++		  method will receive a ++		  <literal>org.freedesktop.realmd.Error.Cancelled</literal> ++		  if the operation was cancelled. ++		--> ++		<method name="Cancel"> ++			<arg name="operation" type="s" direction="in"/> ++		</method> ++ ++		<!-- ++		  SetLocale: ++		  @locale: the locale for the client ++ ++		  Set the language @locale for the client. This locale is used ++		  for error messages. The locale is used until the next time ++		  this method is called, the client disconnects, or the client ++		  calls #org.freedesktop.realmd.Service.Release(). ++		--> ++		<method name="SetLocale"> ++			<arg name="locale" type="s" direction="in"/> ++		</method> ++ ++		<!-- ++		  Diagnostics: ++		  @data: diagnostic data ++		  @operation: the operation this data resulted from ++ ++		  This signal is fired when diagnostics result from an operation ++		  in the provider or one of its realms. ++ ++		  It is not guaranteed that this signal is emitted once per line. ++		  More than one line may be contained in @data, or a partial ++		  line. New line characters are embedded in @data. ++ ++		  This signal is sent explicitly to the client which invoked an ++		  operation method. In order to tell which operation this ++		  diagnostic data results from, pass a unique ++		  <literal>operation</literal> string identifier in the ++		  <literal>options</literal> argument of the operation method. ++		  That same identifier will be passed back via the @operation ++		  argument of this signal. ++		--> ++		<signal name="Diagnostics"> ++			<arg name="data" type="s"/> ++			<arg name="operation" type="s"/> ++		</signal> ++ ++		<!-- ++		  Release: ++ ++		  Normally, realmd waits until all clients have disconnected ++		  before exiting itself sometime later. Long lived clients ++		  can call this method to allow the realmd service to quit. ++		  This is an optimization. The daemon will not exit immediately. ++		  It is safe to call this multiple times. ++		--> ++		<method name="Release"> ++			<!-- no arguments --> ++		</method> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.Realm: ++	  @short_description: a realm ++ ++	  Represents one realm. ++ ++	  Contains generic information about a realm, and useful properties for ++	  introspecting what kind of realm this is and how to work with ++	  the realm. ++ ++	  Use #org.freedesktop.realmd.Provider:Realms or ++	  #org.freedesktop.realmd.Provider.Discover() to get access to some ++	  kerberos realm objects. ++ ++	  Realms will always implement additional interfaces, such as ++	  #org.freedesktop.realmd.Kerberos.  Do not assume that all realms ++	  implement that kerberos interface. Use the ++	  #org.freedesktop.realmd.Realm:SupportedInterfaces property to see ++	  which interfaces are supported. ++ ++	  Different realms support various ways to configure them on the ++	  system. Use the #org.freedesktop.realmd.Realm:Configured property ++	  to determine if a realm is configured. If it is configured, the ++	  property will be set to the interface of the mechanism that was ++	  used to configure it. ++ ++	  To configure a realm, look in the ++	  #org.freedesktop.realmd.Realm:SupportedInterfaces property for a ++	  recognized purpose-specific interface that can be used for ++	  configuration, such as the ++	  #org.freedesktop.realmd.KerberosMembership interface and its ++	  #org.freedesktop.realmd.KerberosMembership.Join() method. ++ ++	  To deconfigure a realm from the current system, you can use the ++	  #org.freedesktop.realmd.Realm.Deconfigure() method. In addition, some ++	  of the configuration specific interfaces provide methods to ++	  deconfigure a realm in a specific way, such as the ++	  #org.freedesktop.realmd.KerberosMembership.Leave() method. ++ ++	  The various properties are guaranteed to have been updated before ++	  the operation methods return, if they change state. ++	--> ++	<interface name="org.freedesktop.realmd.Realm"> ++ ++		<!-- ++		  Name: the realm name ++ ++		  This is the name of the realm, appropriate for display to ++		  end users where necessary. ++		--> ++		<property name="Name" type="s" access="read"/> ++ ++		<!-- ++		  Configured: whether this domain is configured and how ++ ++		  If this property is an empty string, then the realm is not ++		  configured. Otherwise the realm is configured, and contains ++		  a string which is the interface that represents how it was ++		  configured, for example #org.freedesktop.realmd.KerberosMembership. ++		--> ++		<property name="Configured" type="s" access="read"/> ++ ++		<!-- ++		  Deconfigure: deconfigure this realm ++ ++		  Deconfigure this realm from the local machine with standard ++		  default behavior. ++ ++		  The behavior of this method depends on the which configuration ++		  interface is present in the ++		  #org.freedesktop.realmd.Realm.Configured property. It does not ++		  always delete membership accounts in the realm, but just ++		  reconfigures the local machine so it no longer is configured ++		  for the given realm. In some cases the implementation may try ++		  to update membership accounts, but this is not guaranteed. ++ ++		  Various configuration interfaces may support more specific ways ++		  to deconfigure a realm in a specific way, such as the ++		  #org.freedesktop.realmd.KerberosMembership.Leave() method. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.deconfigure-realm</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the deconfigure failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to deconfigure a ++		      realm.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotConfigured</literal>: ++		      returned if this realm is not configured on the machine.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Deconfigure"> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++		<!-- ++		  SupportedInterfaces: ++ ++		  Additional supported interfaces of this realm. This includes ++		  interfaces that contain more information about the realm, ++		  such as #org.freedesktop.realmd.Kerberos and interfaces ++		  which contain methods for configuring a realm, such as ++		  #org.freedesktop.realmd.KerberosMembership. ++		--> ++		<property name="SupportedInterfaces" type="as" access="read"/> ++ ++		<!-- ++		  Details: informational details about the realm ++ ++		  Informational details about the realm. The following values ++		  should be present: ++		  <itemizedlist> ++		    <listitem><para><literal>server-software</literal>: ++		      identifier of the software running on the server (e.g. ++		      <literal>active-directory</literal>).</para></listitem> ++		    <listitem><para><literal>client-software</literal>: ++		      identifier of the software running on the client (e.g. ++		      <literal>sssd</literal>).</para></listitem> ++		  </itemizedlist> ++		--> ++		<property name="Details" type="a(ss)" access="read"/> ++ ++		<!-- ++		  RequiredPackages: prerequisite software ++ ++		  Software packages that are required in order for a join to ++		  succeed. These are either simple strings like <literal>sssd</literal>, ++		  or strings with an operator and version number like ++		  <literal>sssd >= 1.9.0</literal> ++ ++		  These values are specific to the packaging system that is ++		  being run. ++		--> ++		<property name="RequiredPackages" type="as" access="read"/> ++ ++		<!-- ++		  LoginFormats: supported formats for login names ++ ++		  Supported formats for login to this realm. This is only ++		  relevant once the realm has been enrolled. The formats ++		  will contain a <literal>%U</literal> in the string, which ++		  indicate where the user name should be placed. The formats ++		  may contain a <literal>%D</literal> in the string which ++		  indicate where a domain name should be placed. ++ ++		  The first format in the list is the preferred format for ++		  login names. ++		--> ++		<property name="LoginFormats" type="as" access="read"/> ++ ++		<!-- ++		  LoginPolicy: the policy for logins using this realm ++ ++		  The policy for logging into this computer using this realm. ++ ++		  The policy can be changed using the ++		  #org.freedesktop.realmd.Realm.ChangeLoginPolicy() method. ++ ++		  The following policies are predefined. Not all providers ++		  support all these policies and there may be provider specific ++		  policies or multiple policies represented in the string: ++		  <itemizedlist> ++		    <listitem><para><literal>allow-any-login</literal>: allow ++		      login by any authenticated user present in this ++		      realm.</para></listitem> ++		    <listitem><para><literal>allow-realm-logins</literal>: allow ++		      logins according to the realm or domain policy for logins ++		      on this machine. This usually defaults to allowing any realm ++		      user to log in.</para></listitem> ++		    <listitem><para><literal>allow-permitted-logins</literal>: ++		      only allow the logins permitted in the ++		      #org.freedesktop.realmd.Realm:PermittedLogins ++		      property.</para></listitem> ++		    <listitem><para><literal>deny-any-login</literal>: ++		      don't allow any logins via authenticated users of this ++		      realm.</para></listitem> ++		  </itemizedlist> ++		--> ++		<property name="LoginPolicy" type="s" access="read"/> ++ ++		<!-- ++		  PermittedLogins: the permitted login names ++ ++		  The list of permitted authenticated users allowed to login ++		  into this computer. This is only relevant if the ++		  #org.freedesktop.realmd.Realm:LoginPolicy property ++		  contains the <literal>allow-permitted-logins</literal> ++		  string. ++		--> ++		<property name="PermittedLogins" type="as" access="read"/> ++ ++		<!-- ++		  PermittedGroups: the permitted group names ++ ++		  The list of groups which users need to be in to be allowed ++		  to log into this computer. This is only relevant if the ++		  #org.freedesktop.realmd.Realm:LoginPolicy property ++		  contains the <literal>allow-permitted-logins</literal> ++		  string. ++		--> ++		<property name="PermittedGroups" type="as" access="read"/> ++ ++		<!-- ++		  ChangeLoginPolicy: ++		  @login_policy: the new login policy, or an empty string ++		  @permitted_add: a list of logins to permit ++		  @permitted_remove: a list of logins to not permit ++		  @options: options for this operation ++ ++		  Change the login policy and/or permitted logins for this realm. ++ ++		  Not all realms support all the various login policies. An ++		  error will be returned if the new login policy is not supported. ++		  You may specify an empty string for the @login_policy argument ++		  which will cause no change in the policy itself. If the policy ++		  is changed, it will be reflected in the ++		  #org.freedesktop.realmd.Realm:LoginPolicy property. ++ ++		  The @permitted_add and @permitted_remove arguments represent ++		  lists of login names that should be added and removed from ++		  the #org.freedesktop.realmd.Kerberos:PermittedLogins property. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		    <listitem><para><literal>groups</literal>: boolean which if ++		    set to <literal>TRUE</literal> means that the names in ++		    @permitted_add and @permitted_remove are group names instead ++		    of login names.</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.login-policy</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the policy change failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to change login policy ++		      operation.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotConfigured</literal>: ++		      returned if the realm is not configured.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="ChangeLoginPolicy"> ++			<arg name="login_policy" type="s" direction="in"/> ++			<arg name="permitted_add" type="as" direction="in"/> ++			<arg name="permitted_remove" type="as" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.Kerberos: ++	  @short_description: a kerberos realm ++ ++	  An interface that describes a kerberos realm in more detail. This ++	  is always implemented on an DBus object path that also implements ++	  the #org.freedesktop.realmd.Realm interface. ++	--> ++	<interface name="org.freedesktop.realmd.Kerberos"> ++ ++		<!-- ++		  RealmName: the kerberos realm name ++ ++		  The kerberos name for this realm. This is usually in upper ++		  case. ++		--> ++		<property name="RealmName" type="s" access="read"/> ++ ++		<!-- ++		  DomainName: the DNS domain name ++ ++		  The DNS domain name for this realm. ++		--> ++		<property name="DomainName" type="s" access="read"/> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.KerberosMembership: ++ ++	  An interface used to configure this machine by joining a realm. ++ ++	  It sets up a computer/host account in the realm for this machine ++	  and a keytab to track the credentials for that account. ++ ++	  The various properties are guaranteed to have been updated before ++	  the operation methods return, if they change state. ++	--> ++	<interface name="org.freedesktop.realmd.KerberosMembership"> ++ ++		<!-- ++		  SuggestedAdministrator: common administrator name ++ ++		  The common administrator name for this type of realm. This ++		  can be used by clients as a hint when prompting the user for ++		  administrative authentication. ++		--> ++		<property name="SuggestedAdministrator" type="s" access="read"/> ++ ++		<!-- ++		  SupportedJoinCredentials: credentials supported for joining ++ ++		  Various kinds of credentials that are supported when calling the ++		  #org.freedesktop.realmd.Kerberos.Join() method. ++ ++		  Each credential is represented by a type and an owner. The type ++		  denotes which kind of credential is passed to the method. The ++		  owner indicates to the client how to prompt the user or obtain ++		  the credential, and to the service how to use the credential. ++ ++		  The various types are: ++		  <itemizedlist> ++		    <listitem><para><literal>ccache</literal>: ++		      the credentials should contain an array of bytes as a ++		      <literal>ay</literal> containing the data from a kerberos ++		      credential cache file.</para></listitem> ++		    <listitem><para><literal>password</literal>: ++		      the credentials should contain a pair of strings as a ++		      <literal>(ss)</literal> representing a name and ++		      password. The name may contain a realm in the standard ++		      kerberos format. If a realm is missing, it will default ++		      to this realm. </para></listitem> ++		    <listitem><para><literal>secret</literal>: ++		      the credentials should contain a string secret as an ++		      <literal>ay</literal> array of bytes. This is usually used ++		      for one time passwords. To pass a string here, encode it ++		      in UTF-8, and place the resulting bytes in the ++		      value.</para></listitem> ++		    <listitem><para><literal>automatic</literal>: ++		      the credentials should contain an empty string as a ++		      <literal>s</literal>. Using <literal>automatic</literal> ++		      indicates that default or system credentials are to be ++		      used.</para></listitem> ++		  </itemizedlist> ++ ++		  The various owners are: ++		  <itemizedlist> ++		    <listitem><para><literal>administrator</literal>: ++		      the credentials belong to a kerberos administrator principal. ++		      The caller may use this as a hint to prompt the user ++		      for administrative credentials.</para></listitem> ++		    <listitem><para><literal>user</literal>: ++		      the credentials belong to a kerberos user principal. ++		      The caller may use this as a hint to prompt the user ++		      for his (possibly non-administrative) ++		      credentials.</para></listitem> ++		    <listitem><para><literal>computer</literal>: ++		      the credentials belong to a computer account.</para></listitem> ++		    <listitem><para><literal>none</literal>: ++		      the credentials have an unspecified owner, such as a one ++		      time password.</para></listitem> ++		  </itemizedlist> ++		--> ++		<property name="SupportedJoinCredentials" type="a(ss)" access="read"/> ++ ++		<!-- ++		  SupportedLeaveCredentials: credentials supported for leaving ++ ++		  Various kinds of credentials that are supported when calling the ++		  #org.freedesktop.realmd.Kerberos.Leave() method. ++ ++		  See #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials for ++		  a discussion of what the values represent. ++		--> ++		<property name="SupportedLeaveCredentials" type="a(ss)" access="read"/> ++ ++		<!-- ++		  Join: ++ ++		  Join this machine to the realm and enroll the machine. ++ ++		  If this method returns successfully, then the machine will be ++		  joined to the realm. It is not necessary to restart services or the ++		  machine afterward. Relevant properties on the realm will be updated ++		  before the method returns. ++ ++		  The @credentials should be set according to one of the ++		  supported credentials returned by ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials. ++		  The first string in the tuple is the type, the second string ++		  is the owner, and the variant contains the credential contents ++		  See the discussion at ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials ++		  for more information. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++                    <listitem><para><literal>automatic-id-mapping</literal>: a boolean ++                      value whether to turn on automatic UID/GID mapping. If not ++		      specified the default will come from realmd.conf ++                      configuration.</para></listitem> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		    <listitem><para><literal>computer-ou</literal>: a string ++		      containing an LDAP DN for an organizational unit where the ++		      computer account should be created</para></listitem> ++		    <listitem><para><literal>user-principal</literal>: a string ++		      containing an kerberos user principal name to be set on the ++		      computer account</para></listitem> ++		    <listitem><para><literal>membership-software</literal>: a string ++		      containing the membership software identifier that the returned ++		      realms should match.</para></listitem> ++		    <listitem><para><literal>manage-system</literal>: a boolean ++		      which controls whether this machine should be managed by ++		      the realm or domain or not. Defaults to true.</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.configure-realm</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the join failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to perform a join ++		      operation.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.AuthenticationFailed</literal>: ++		      returned if the credentials passed did not authenticate against the realm ++		      correctly. It is appropriate to prompt the user again.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.AlreadyEnrolled</literal>: ++		      returned if already enrolled in this realm, or if already enrolled in another realm ++		      (if enrolling in multiple realms is not supported).</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.BadHostname</literal>: ++		      returned if the machine has a hostname that is not usable for a join ++		      or is in conflict with those in the domain.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Join"> ++			<arg name="credentials" type="(ssv)" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++		<!-- ++		  Leave: ++ ++		  Leave the realm and unenroll the machine. ++ ++		  If this method returns successfully, then the machine will have ++		  left the domain and been unenrolled. It is not necessary to restart ++		  services or the machine afterward. Relevant properties on the realm ++		  will be updated before the method returns. ++ ++		  The @credentials should be set according to one of the ++		  supported credentials returned by ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials. ++		  The first string in the tuple is the type, the second string ++		  is the owner, and the variant contains the credential contents ++		  See the discussion at ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials ++		  for more information. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.deconfigure-realm</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the unenroll failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to perform an unenroll ++		      operation.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.AuthenticationFailed</literal>: ++		      returned if the credentials passed did not authenticate against the realm ++		      correctly. It is appropriate to prompt the user again.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotEnrolled</literal>: ++		      returned if not enrolled in this realm.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Leave"> ++			<arg name="credentials" type="(ssv)" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++	</interface> ++ ++</node> +diff --git a/src/daemon.c b/src/daemon.c +index c52bda3..5ce0216 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -20,60 +20,61 @@ +  * Written by: Matthias Clasen <mclasen@redhat.com> +  */ +  + #include "config.h" +  + #include <stdlib.h> + #include <stdio.h> + #include <sys/types.h> + #include <sys/stat.h> + #include <fcntl.h> + #include <sys/wait.h> + #include <pwd.h> + #ifdef HAVE_SHADOW_H + #include <shadow.h> + #endif + #include <unistd.h> + #include <errno.h> + #include <sys/types.h> +  + #include <glib.h> + #include <glib/gi18n.h> + #include <glib-object.h> + #include <glib/gstdio.h> + #include <gio/gio.h> + #include <polkit/polkit.h> +  + #include "user-classify.h" + #include "wtmp-helper.h" + #include "daemon.h" + #include "util.h" ++#include "realmd-generated.h" +  + #define PATH_PASSWD "/etc/passwd" + #define PATH_SHADOW "/etc/shadow" + #define PATH_GROUP "/etc/group" +  + enum { +         PROP_0, +         PROP_DAEMON_VERSION + }; +  + typedef struct { +         GDBusConnection *bus_connection; +  +         GHashTable *users; +         gsize number_of_normal_users; +         GList *explicitly_requested_users; +  +         User *autologin; +  +         GFileMonitor *passwd_monitor; +         GFileMonitor *shadow_monitor; +         GFileMonitor *group_monitor; +         GFileMonitor *gdm_monitor; +         GFileMonitor *wtmp_monitor; +  +         GQueue *pending_list_cached_users; +  +         guint reload_id; +         guint autologin_id; +  +@@ -425,110 +426,167 @@ load_entries (Daemon             *daemon, +                         } else { +                                 g_object_ref (user); +                         } +  +                         /* freeze & update users not already in the new list */ +                         g_object_freeze_notify (G_OBJECT (user)); +                         user_update_from_pwent (user, pwent, spent); +  +                         g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user); +                         g_debug ("loaded user: %s", user_get_user_name (user)); +                 } +  +                 if (!explicitly_requested) { +                         user_set_cached (user, TRUE); +                 } +         } +  +         /* Generator should have cleaned up */ +         g_assert (generator_state == NULL); + } +  + static GHashTable * + create_users_hash_table (void) + { +         return g_hash_table_new_full (g_str_hash, +                                       g_str_equal, +                                       g_free, +                                       g_object_unref); + } +  ++static gboolean ++ensure_bus_connection (Daemon *daemon) ++{ ++        DaemonPrivate *priv = daemon_get_instance_private (daemon); ++        g_autoptr (GError) error = NULL; ++ ++        if (priv->bus_connection != NULL) ++                return TRUE; ++ ++        priv->bus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); ++        if (priv->bus_connection == NULL) { ++                if (error != NULL) ++                        g_critical ("error getting system bus: %s", error->message); ++                return FALSE; ++        } ++ ++        return TRUE; ++} ++ ++static gboolean ++has_network_realms (Daemon *daemon) ++{ ++        DaemonPrivate *priv = daemon_get_instance_private (daemon); ++        g_autoptr (AccountsRealmdProvider) realmd_provider = NULL; ++        g_autoptr (GError) error = NULL; ++        const char *const *realms = NULL; ++ ++        if (!ensure_bus_connection (daemon)) { ++                return FALSE; ++        } ++ ++        realmd_provider = accounts_realmd_provider_proxy_new_sync (priv->bus_connection, ++                                                                   G_DBUS_PROXY_FLAGS_NONE, ++                                                                   "org.freedesktop.realmd", ++                                                                   "/org/freedesktop/realmd", ++                                                                   NULL, ++                                                                   &error); ++        if (realmd_provider == NULL) { ++                g_debug ("failed to contact realmd: %s", error->message); ++                return FALSE; ++        } ++ ++        realms = accounts_realmd_provider_get_realms (realmd_provider); ++ ++        if (!realms) { ++                g_debug("realmd provider 'Realms' property is unset"); ++                return FALSE; ++        } ++ ++        return realms[0] != NULL; ++} ++ + static void + reload_users (Daemon *daemon) + { +         DaemonPrivate *priv = daemon_get_instance_private (daemon); +         AccountsAccounts *accounts = ACCOUNTS_ACCOUNTS (daemon); +         gboolean had_no_users, has_no_users, had_multiple_users, has_multiple_users; +         GHashTable *users; +         GHashTable *old_users; +         GHashTable *local; +         GHashTableIter iter; +         gsize number_of_normal_users = 0; +         gpointer name, value; +  +         /* Track the users that we saw during our (re)load */ +         users = create_users_hash_table (); +  +         /* +          * NOTE: As we load data from all the sources, notifies are +          * frozen in load_entries() and then thawed as we process +          * them below. +          */ +  +         /* Load the local users into our hash table */ +         load_entries (daemon, users, FALSE, entry_generator_fgetpwent); +         local = g_hash_table_new (g_str_hash, g_str_equal); +         g_hash_table_iter_init (&iter, users); +         while (g_hash_table_iter_next (&iter, &name, NULL)) +                 g_hash_table_add (local, name); +  +         /* and add users to hash table that were explicitly requested  */ +         load_entries (daemon, users, TRUE, entry_generator_requested_users); +  +         /* Now add/update users from other sources, possibly non-local */ +         load_entries (daemon, users, FALSE, entry_generator_cachedir); +  +         wtmp_helper_update_login_frequencies (users); +  +         /* Count the non-system users. Mark which users are local, which are not. */ +         g_hash_table_iter_init (&iter, users); +         while (g_hash_table_iter_next (&iter, &name, &value)) { +                 User *user = value; +                 if (!user_get_system_account (user)) +                         number_of_normal_users++; +                 user_update_local_account_property (user, g_hash_table_lookup (local, name) != NULL); +         } +         g_hash_table_destroy (local); +  +         had_no_users = accounts_accounts_get_has_no_users (accounts); +         has_no_users = number_of_normal_users == 0; +  ++        if (has_no_users && has_network_realms (daemon)) { ++                g_debug ("No local users, but network realms detected, presuming there are remote users"); ++                has_no_users = FALSE; ++        } ++ +         if (had_no_users != has_no_users) +                 accounts_accounts_set_has_no_users (accounts, has_no_users); +  +         had_multiple_users = accounts_accounts_get_has_multiple_users (accounts); +         has_multiple_users = number_of_normal_users > 1; +  +         if (had_multiple_users != has_multiple_users) +                 accounts_accounts_set_has_multiple_users (accounts, has_multiple_users); +  +         /* Swap out the users */ +         old_users = priv->users; +         priv->users = users; +  +         /* Remove all the old users */ +         g_hash_table_iter_init (&iter, old_users); +         while (g_hash_table_iter_next (&iter, &name, &value)) { +                 User *user = value; +                 User *refreshed_user; +  +                 refreshed_user = g_hash_table_lookup (users, name); +  +                 if (!refreshed_user || (user_get_cached (user) && !user_get_cached (refreshed_user))) { +                         accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon), +                                                              user_get_object_path (user)); +                         user_unregister (user); +                 } +         } +  +         /* Register all the new users */ +         g_hash_table_iter_init (&iter, users); +@@ -766,64 +824,61 @@ daemon_finalize (GObject *object) +         priv = daemon_get_instance_private (daemon); +  +         if (priv->bus_connection != NULL) +                 g_object_unref (priv->bus_connection); +  +         g_queue_free_full (priv->pending_list_cached_users, +                            (GDestroyNotify) list_user_data_free); +  +         g_list_free_full (priv->explicitly_requested_users, g_free); +  +         g_hash_table_destroy (priv->users); +  +         g_hash_table_unref (priv->extension_ifaces); +  +         G_OBJECT_CLASS (daemon_parent_class)->finalize (object); + } +  + static gboolean + register_accounts_daemon (Daemon *daemon) + { +         DaemonPrivate *priv = daemon_get_instance_private (daemon); +         g_autoptr(GError) error = NULL; +  +         priv->authority = polkit_authority_get_sync (NULL, &error); +         if (priv->authority == NULL) { +                 if (error != NULL) +                         g_critical ("error getting polkit authority: %s", error->message); +                 return FALSE; +         } +  +-        priv->bus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); +-        if (priv->bus_connection == NULL) { +-                if (error != NULL) +-                        g_critical ("error getting system bus: %s", error->message); ++        if (!ensure_bus_connection (daemon)) { +                 return FALSE; +         } +  +         if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon), +                                                priv->bus_connection, +                                                "/org/freedesktop/Accounts", +                                                &error)) { +                 if (error != NULL) +                         g_critical ("error exporting interface: %s", error->message); +                 return FALSE; +         } +  +         return TRUE; + } +  + Daemon * + daemon_new (void) + { +         g_autoptr(Daemon) daemon = NULL; +  +         daemon = DAEMON (g_object_new (TYPE_DAEMON, NULL)); +  +         if (!register_accounts_daemon (DAEMON (daemon))) { +                 return NULL; +         } +  +         return g_steal_pointer (&daemon); + } +  + static void +diff --git a/src/meson.build b/src/meson.build +index 20d5276..3970749 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -1,37 +1,38 @@ + sources = [] +  + gdbus_headers = [] +  + ifaces = [ +   ['accounts-generated', 'org.freedesktop.', 'Accounts'], +   ['accounts-user-generated', act_namespace + '.', 'User'], ++  ['realmd-generated', 'org.freedesktop.', 'realmd'], + ] +  + foreach iface: ifaces +   gdbus_sources = gnome.gdbus_codegen( +     iface[0], +     join_paths(data_dir, iface[1] + iface[2] + '.xml'), +     interface_prefix: iface[1], +     namespace: 'Accounts', +   ) +   sources += gdbus_sources +   gdbus_headers += gdbus_sources[1] + endforeach +  + deps = [ +   gio_dep, +   gio_unix_dep, + ] +  + cflags = [ +   '-DLOCALSTATEDIR="@0@"'.format(act_localstatedir), +   '-DDATADIR="@0@"'.format(act_datadir), +   '-DICONDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'icons')), +   '-DUSERDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'users')), + ] +  + libaccounts_generated = static_library( +   'accounts-generated', +   sources: sources, +   include_directories: top_inc, +   dependencies: deps, +diff --git a/src/org.freedesktop.realmd.xml b/src/org.freedesktop.realmd.xml +new file mode 100644 +index 0000000..c34a47a +--- /dev/null ++++ b/src/org.freedesktop.realmd.xml +@@ -0,0 +1,730 @@ ++<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" ++ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> ++<node name="/"> ++ ++	<!-- ++	  org.freedesktop.realmd.Provider: ++	  @short_description: a realm provider ++ ++	  Various realm providers represent different software implementations ++	  that provide access to realms or domains. ++ ++	  This interface is implemented by individual providers, but is ++	  aggregated globally at the system bus name ++	  <literal>org.freedesktop.realmd</literal> ++	  with the object path <literal>/org/freedesktop/realmd</literal> ++	--> ++	<interface name="org.freedesktop.realmd.Provider"> ++ ++		<!-- ++		  Name: the name of the provider ++ ++		  The name of the provider. This is not normally displayed ++		  to the user, but may be useful for diagnostics or debugging. ++		--> ++		<property name="Name" type="s" access="read"/> ++ ++		<!-- ++		  Version: the version of the provider ++ ++		  The version of the provider. This is not normally used in ++		  logic, but may be useful for diagnostics or debugging. ++		--> ++		<property name="Version" type="s" access="read"/> ++ ++		<!-- ++		  Realms: a list of realms ++ ++		  A list of known, enrolled or discovered realms. All realms ++		  that this provider knows about are listed here. As realms ++		  are discovered they are added to this list. ++ ++		  Each realm is represented by the DBus object path of the ++		  realm object. ++		--> ++		<property name="Realms" type="ao" access="read"/> ++ ++		<!-- ++		  Discover: ++		  @string: an input string to discover realms for ++		  @options: options for the discovery operation ++		  @relevance: the relevance of the returned results ++		  @realm: a list of realms discovered ++ ++		  Discover realms for the given string. The input @string is ++		  usually a domain or realm name, perhaps typed by a user. If ++		  an empty string is provided, the realm provider should try to ++		  discover a default realm, if possible (e.g. from DHCP). ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		    <listitem><para><literal>client-software</literal>: a string ++		      containing the client software identifier that the returned ++		      realms should match.</para></listitem> ++		    <listitem><para><literal>server-software</literal>: a string ++		      containing the client software identifier that the returned ++		      realms should match.</para></listitem> ++		    <listitem><para><literal>membership-software</literal>: a string ++		      containing the membership software identifier that the returned ++		      realms should match.</para></listitem> ++		  </itemizedlist> ++ ++		  The @relevance returned can be used to rank results from ++		  different discover calls to different providers. Implementors ++		  should return a positive number if the provider highly ++		  recommends that the realms be handled by this provider, ++		  or a zero if it can possibly handle the realms. Negative numbers ++		  should be returned if no realms are found. ++ ++		  This method does not return an error when no realms are ++		  discovered. It simply returns an empty @realm list. ++ ++		  To see diagnostic information about the discovery process, ++		  connect to the org.freedesktop.realmd.Service::Diagnostics ++		  signal. ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.discover-realm</literal>. ++ ++		  In addition to common DBus error results, this method may ++		  return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the discovery could not be run for some reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to perform a discovery ++		      operation.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Discover"> ++			<arg name="string" type="s" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++			<arg name="relevance" type="i" direction="out"/> ++			<arg name="realm" type="ao" direction="out"/> ++		</method> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.Service: ++	  @short_description: the realmd service ++ ++	  Global calls for managing the realmd service. Usually you'll want ++	  to use #org.freedesktop.realmd.Provider instead. ++ ++	  This interface is implemented by the realmd service, and is always ++	  available at the object path <literal>/org/freedesktop/realmd</literal> ++ ++	  The service also implements the ++	  <literal>org.freedesktop.DBus.ObjectManager</literal> interface which ++	  makes it easy to retrieve all realmd objects and properties in one go. ++	--> ++	<interface name="org.freedesktop.realmd.Service"> ++ ++		<!-- ++		  Cancel: ++		  @operation: the operation to cancel ++ ++		  Cancel a realmd operation. To be able to cancel an operation, ++		  pass a uniquely chosen <literal>operation</literal> string ++		  identifier as an option in the method's <literal>options</literal> ++		  argument. ++ ++		  These operation string identifiers should be unique per client ++		  calling the realmd service. ++ ++		  It is not guaranteed that the service can or will cancel the ++		  operation. For example, the operation may have already completed ++		  by the time this method is handled. The caller of the operation ++		  method will receive a ++		  <literal>org.freedesktop.realmd.Error.Cancelled</literal> ++		  if the operation was cancelled. ++		--> ++		<method name="Cancel"> ++			<arg name="operation" type="s" direction="in"/> ++		</method> ++ ++		<!-- ++		  SetLocale: ++		  @locale: the locale for the client ++ ++		  Set the language @locale for the client. This locale is used ++		  for error messages. The locale is used until the next time ++		  this method is called, the client disconnects, or the client ++		  calls #org.freedesktop.realmd.Service.Release(). ++		--> ++		<method name="SetLocale"> ++			<arg name="locale" type="s" direction="in"/> ++		</method> ++ ++		<!-- ++		  Diagnostics: ++		  @data: diagnostic data ++		  @operation: the operation this data resulted from ++ ++		  This signal is fired when diagnostics result from an operation ++		  in the provider or one of its realms. ++ ++		  It is not guaranteed that this signal is emitted once per line. ++		  More than one line may be contained in @data, or a partial ++		  line. New line characters are embedded in @data. ++ ++		  This signal is sent explicitly to the client which invoked an ++		  operation method. In order to tell which operation this ++		  diagnostic data results from, pass a unique ++		  <literal>operation</literal> string identifier in the ++		  <literal>options</literal> argument of the operation method. ++		  That same identifier will be passed back via the @operation ++		  argument of this signal. ++		--> ++		<signal name="Diagnostics"> ++			<arg name="data" type="s"/> ++			<arg name="operation" type="s"/> ++		</signal> ++ ++		<!-- ++		  Release: ++ ++		  Normally, realmd waits until all clients have disconnected ++		  before exiting itself sometime later. Long lived clients ++		  can call this method to allow the realmd service to quit. ++		  This is an optimization. The daemon will not exit immediately. ++		  It is safe to call this multiple times. ++		--> ++		<method name="Release"> ++			<!-- no arguments --> ++		</method> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.Realm: ++	  @short_description: a realm ++ ++	  Represents one realm. ++ ++	  Contains generic information about a realm, and useful properties for ++	  introspecting what kind of realm this is and how to work with ++	  the realm. ++ ++	  Use #org.freedesktop.realmd.Provider:Realms or ++	  #org.freedesktop.realmd.Provider.Discover() to get access to some ++	  kerberos realm objects. ++ ++	  Realms will always implement additional interfaces, such as ++	  #org.freedesktop.realmd.Kerberos.  Do not assume that all realms ++	  implement that kerberos interface. Use the ++	  #org.freedesktop.realmd.Realm:SupportedInterfaces property to see ++	  which interfaces are supported. ++ ++	  Different realms support various ways to configure them on the ++	  system. Use the #org.freedesktop.realmd.Realm:Configured property ++	  to determine if a realm is configured. If it is configured, the ++	  property will be set to the interface of the mechanism that was ++	  used to configure it. ++ ++	  To configure a realm, look in the ++	  #org.freedesktop.realmd.Realm:SupportedInterfaces property for a ++	  recognized purpose-specific interface that can be used for ++	  configuration, such as the ++	  #org.freedesktop.realmd.KerberosMembership interface and its ++	  #org.freedesktop.realmd.KerberosMembership.Join() method. ++ ++	  To deconfigure a realm from the current system, you can use the ++	  #org.freedesktop.realmd.Realm.Deconfigure() method. In addition, some ++	  of the configuration specific interfaces provide methods to ++	  deconfigure a realm in a specific way, such as the ++	  #org.freedesktop.realmd.KerberosMembership.Leave() method. ++ ++	  The various properties are guaranteed to have been updated before ++	  the operation methods return, if they change state. ++	--> ++	<interface name="org.freedesktop.realmd.Realm"> ++ ++		<!-- ++		  Name: the realm name ++ ++		  This is the name of the realm, appropriate for display to ++		  end users where necessary. ++		--> ++		<property name="Name" type="s" access="read"/> ++ ++		<!-- ++		  Configured: whether this domain is configured and how ++ ++		  If this property is an empty string, then the realm is not ++		  configured. Otherwise the realm is configured, and contains ++		  a string which is the interface that represents how it was ++		  configured, for example #org.freedesktop.realmd.KerberosMembership. ++		--> ++		<property name="Configured" type="s" access="read"/> ++ ++		<!-- ++		  Deconfigure: deconfigure this realm ++ ++		  Deconfigure this realm from the local machine with standard ++		  default behavior. ++ ++		  The behavior of this method depends on the which configuration ++		  interface is present in the ++		  #org.freedesktop.realmd.Realm.Configured property. It does not ++		  always delete membership accounts in the realm, but just ++		  reconfigures the local machine so it no longer is configured ++		  for the given realm. In some cases the implementation may try ++		  to update membership accounts, but this is not guaranteed. ++ ++		  Various configuration interfaces may support more specific ways ++		  to deconfigure a realm in a specific way, such as the ++		  #org.freedesktop.realmd.KerberosMembership.Leave() method. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.deconfigure-realm</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the deconfigure failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to deconfigure a ++		      realm.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotConfigured</literal>: ++		      returned if this realm is not configured on the machine.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Deconfigure"> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++		<!-- ++		  SupportedInterfaces: ++ ++		  Additional supported interfaces of this realm. This includes ++		  interfaces that contain more information about the realm, ++		  such as #org.freedesktop.realmd.Kerberos and interfaces ++		  which contain methods for configuring a realm, such as ++		  #org.freedesktop.realmd.KerberosMembership. ++		--> ++		<property name="SupportedInterfaces" type="as" access="read"/> ++ ++		<!-- ++		  Details: informational details about the realm ++ ++		  Informational details about the realm. The following values ++		  should be present: ++		  <itemizedlist> ++		    <listitem><para><literal>server-software</literal>: ++		      identifier of the software running on the server (e.g. ++		      <literal>active-directory</literal>).</para></listitem> ++		    <listitem><para><literal>client-software</literal>: ++		      identifier of the software running on the client (e.g. ++		      <literal>sssd</literal>).</para></listitem> ++		  </itemizedlist> ++		--> ++		<property name="Details" type="a(ss)" access="read"/> ++ ++		<!-- ++		  RequiredPackages: prerequisite software ++ ++		  Software packages that are required in order for a join to ++		  succeed. These are either simple strings like <literal>sssd</literal>, ++		  or strings with an operator and version number like ++		  <literal>sssd >= 1.9.0</literal> ++ ++		  These values are specific to the packaging system that is ++		  being run. ++		--> ++		<property name="RequiredPackages" type="as" access="read"/> ++ ++		<!-- ++		  LoginFormats: supported formats for login names ++ ++		  Supported formats for login to this realm. This is only ++		  relevant once the realm has been enrolled. The formats ++		  will contain a <literal>%U</literal> in the string, which ++		  indicate where the user name should be placed. The formats ++		  may contain a <literal>%D</literal> in the string which ++		  indicate where a domain name should be placed. ++ ++		  The first format in the list is the preferred format for ++		  login names. ++		--> ++		<property name="LoginFormats" type="as" access="read"/> ++ ++		<!-- ++		  LoginPolicy: the policy for logins using this realm ++ ++		  The policy for logging into this computer using this realm. ++ ++		  The policy can be changed using the ++		  #org.freedesktop.realmd.Realm.ChangeLoginPolicy() method. ++ ++		  The following policies are predefined. Not all providers ++		  support all these policies and there may be provider specific ++		  policies or multiple policies represented in the string: ++		  <itemizedlist> ++		    <listitem><para><literal>allow-any-login</literal>: allow ++		      login by any authenticated user present in this ++		      realm.</para></listitem> ++		    <listitem><para><literal>allow-realm-logins</literal>: allow ++		      logins according to the realm or domain policy for logins ++		      on this machine. This usually defaults to allowing any realm ++		      user to log in.</para></listitem> ++		    <listitem><para><literal>allow-permitted-logins</literal>: ++		      only allow the logins permitted in the ++		      #org.freedesktop.realmd.Realm:PermittedLogins ++		      property.</para></listitem> ++		    <listitem><para><literal>deny-any-login</literal>: ++		      don't allow any logins via authenticated users of this ++		      realm.</para></listitem> ++		  </itemizedlist> ++		--> ++		<property name="LoginPolicy" type="s" access="read"/> ++ ++		<!-- ++		  PermittedLogins: the permitted login names ++ ++		  The list of permitted authenticated users allowed to login ++		  into this computer. This is only relevant if the ++		  #org.freedesktop.realmd.Realm:LoginPolicy property ++		  contains the <literal>allow-permitted-logins</literal> ++		  string. ++		--> ++		<property name="PermittedLogins" type="as" access="read"/> ++ ++		<!-- ++		  PermittedGroups: the permitted group names ++ ++		  The list of groups which users need to be in to be allowed ++		  to log into this computer. This is only relevant if the ++		  #org.freedesktop.realmd.Realm:LoginPolicy property ++		  contains the <literal>allow-permitted-logins</literal> ++		  string. ++		--> ++		<property name="PermittedGroups" type="as" access="read"/> ++ ++		<!-- ++		  ChangeLoginPolicy: ++		  @login_policy: the new login policy, or an empty string ++		  @permitted_add: a list of logins to permit ++		  @permitted_remove: a list of logins to not permit ++		  @options: options for this operation ++ ++		  Change the login policy and/or permitted logins for this realm. ++ ++		  Not all realms support all the various login policies. An ++		  error will be returned if the new login policy is not supported. ++		  You may specify an empty string for the @login_policy argument ++		  which will cause no change in the policy itself. If the policy ++		  is changed, it will be reflected in the ++		  #org.freedesktop.realmd.Realm:LoginPolicy property. ++ ++		  The @permitted_add and @permitted_remove arguments represent ++		  lists of login names that should be added and removed from ++		  the #org.freedesktop.realmd.Kerberos:PermittedLogins property. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		    <listitem><para><literal>groups</literal>: boolean which if ++		    set to <literal>TRUE</literal> means that the names in ++		    @permitted_add and @permitted_remove are group names instead ++		    of login names.</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.login-policy</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the policy change failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to change login policy ++		      operation.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotConfigured</literal>: ++		      returned if the realm is not configured.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="ChangeLoginPolicy"> ++			<arg name="login_policy" type="s" direction="in"/> ++			<arg name="permitted_add" type="as" direction="in"/> ++			<arg name="permitted_remove" type="as" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.Kerberos: ++	  @short_description: a kerberos realm ++ ++	  An interface that describes a kerberos realm in more detail. This ++	  is always implemented on an DBus object path that also implements ++	  the #org.freedesktop.realmd.Realm interface. ++	--> ++	<interface name="org.freedesktop.realmd.Kerberos"> ++ ++		<!-- ++		  RealmName: the kerberos realm name ++ ++		  The kerberos name for this realm. This is usually in upper ++		  case. ++		--> ++		<property name="RealmName" type="s" access="read"/> ++ ++		<!-- ++		  DomainName: the DNS domain name ++ ++		  The DNS domain name for this realm. ++		--> ++		<property name="DomainName" type="s" access="read"/> ++ ++	</interface> ++ ++	<!-- ++	  org.freedesktop.realmd.KerberosMembership: ++ ++	  An interface used to configure this machine by joining a realm. ++ ++	  It sets up a computer/host account in the realm for this machine ++	  and a keytab to track the credentials for that account. ++ ++	  The various properties are guaranteed to have been updated before ++	  the operation methods return, if they change state. ++	--> ++	<interface name="org.freedesktop.realmd.KerberosMembership"> ++ ++		<!-- ++		  SuggestedAdministrator: common administrator name ++ ++		  The common administrator name for this type of realm. This ++		  can be used by clients as a hint when prompting the user for ++		  administrative authentication. ++		--> ++		<property name="SuggestedAdministrator" type="s" access="read"/> ++ ++		<!-- ++		  SupportedJoinCredentials: credentials supported for joining ++ ++		  Various kinds of credentials that are supported when calling the ++		  #org.freedesktop.realmd.Kerberos.Join() method. ++ ++		  Each credential is represented by a type and an owner. The type ++		  denotes which kind of credential is passed to the method. The ++		  owner indicates to the client how to prompt the user or obtain ++		  the credential, and to the service how to use the credential. ++ ++		  The various types are: ++		  <itemizedlist> ++		    <listitem><para><literal>ccache</literal>: ++		      the credentials should contain an array of bytes as a ++		      <literal>ay</literal> containing the data from a kerberos ++		      credential cache file.</para></listitem> ++		    <listitem><para><literal>password</literal>: ++		      the credentials should contain a pair of strings as a ++		      <literal>(ss)</literal> representing a name and ++		      password. The name may contain a realm in the standard ++		      kerberos format. If a realm is missing, it will default ++		      to this realm. </para></listitem> ++		    <listitem><para><literal>secret</literal>: ++		      the credentials should contain a string secret as an ++		      <literal>ay</literal> array of bytes. This is usually used ++		      for one time passwords. To pass a string here, encode it ++		      in UTF-8, and place the resulting bytes in the ++		      value.</para></listitem> ++		    <listitem><para><literal>automatic</literal>: ++		      the credentials should contain an empty string as a ++		      <literal>s</literal>. Using <literal>automatic</literal> ++		      indicates that default or system credentials are to be ++		      used.</para></listitem> ++		  </itemizedlist> ++ ++		  The various owners are: ++		  <itemizedlist> ++		    <listitem><para><literal>administrator</literal>: ++		      the credentials belong to a kerberos administrator principal. ++		      The caller may use this as a hint to prompt the user ++		      for administrative credentials.</para></listitem> ++		    <listitem><para><literal>user</literal>: ++		      the credentials belong to a kerberos user principal. ++		      The caller may use this as a hint to prompt the user ++		      for his (possibly non-administrative) ++		      credentials.</para></listitem> ++		    <listitem><para><literal>computer</literal>: ++		      the credentials belong to a computer account.</para></listitem> ++		    <listitem><para><literal>none</literal>: ++		      the credentials have an unspecified owner, such as a one ++		      time password.</para></listitem> ++		  </itemizedlist> ++		--> ++		<property name="SupportedJoinCredentials" type="a(ss)" access="read"/> ++ ++		<!-- ++		  SupportedLeaveCredentials: credentials supported for leaving ++ ++		  Various kinds of credentials that are supported when calling the ++		  #org.freedesktop.realmd.Kerberos.Leave() method. ++ ++		  See #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials for ++		  a discussion of what the values represent. ++		--> ++		<property name="SupportedLeaveCredentials" type="a(ss)" access="read"/> ++ ++		<!-- ++		  Join: ++ ++		  Join this machine to the realm and enroll the machine. ++ ++		  If this method returns successfully, then the machine will be ++		  joined to the realm. It is not necessary to restart services or the ++		  machine afterward. Relevant properties on the realm will be updated ++		  before the method returns. ++ ++		  The @credentials should be set according to one of the ++		  supported credentials returned by ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials. ++		  The first string in the tuple is the type, the second string ++		  is the owner, and the variant contains the credential contents ++		  See the discussion at ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials ++		  for more information. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++                    <listitem><para><literal>automatic-id-mapping</literal>: a boolean ++                      value whether to turn on automatic UID/GID mapping. If not ++		      specified the default will come from realmd.conf ++                      configuration.</para></listitem> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		    <listitem><para><literal>computer-ou</literal>: a string ++		      containing an LDAP DN for an organizational unit where the ++		      computer account should be created</para></listitem> ++		    <listitem><para><literal>user-principal</literal>: a string ++		      containing an kerberos user principal name to be set on the ++		      computer account</para></listitem> ++		    <listitem><para><literal>membership-software</literal>: a string ++		      containing the membership software identifier that the returned ++		      realms should match.</para></listitem> ++		    <listitem><para><literal>manage-system</literal>: a boolean ++		      which controls whether this machine should be managed by ++		      the realm or domain or not. Defaults to true.</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.configure-realm</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the join failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to perform a join ++		      operation.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.AuthenticationFailed</literal>: ++		      returned if the credentials passed did not authenticate against the realm ++		      correctly. It is appropriate to prompt the user again.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.AlreadyEnrolled</literal>: ++		      returned if already enrolled in this realm, or if already enrolled in another realm ++		      (if enrolling in multiple realms is not supported).</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.BadHostname</literal>: ++		      returned if the machine has a hostname that is not usable for a join ++		      or is in conflict with those in the domain.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Join"> ++			<arg name="credentials" type="(ssv)" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++		<!-- ++		  Leave: ++ ++		  Leave the realm and unenroll the machine. ++ ++		  If this method returns successfully, then the machine will have ++		  left the domain and been unenrolled. It is not necessary to restart ++		  services or the machine afterward. Relevant properties on the realm ++		  will be updated before the method returns. ++ ++		  The @credentials should be set according to one of the ++		  supported credentials returned by ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials. ++		  The first string in the tuple is the type, the second string ++		  is the owner, and the variant contains the credential contents ++		  See the discussion at ++		  #org.freedesktop.realmd.Kerberos:SupportedJoinCredentials ++		  for more information. ++ ++		  @options can contain, but is not limited to, the following values: ++		  <itemizedlist> ++		    <listitem><para><literal>operation</literal>: a string ++		      identifier chosen by the client, which can then later be ++		      passed to org.freedesktop.realmd.Service.Cancel() in order ++		      to cancel the operation</para></listitem> ++		  </itemizedlist> ++ ++		  This method requires authorization for the PolicyKit action ++		  called <literal>org.freedesktop.realmd.deconfigure-realm</literal>. ++ ++		  In addition to common DBus error results, this method may return: ++		  <itemizedlist> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Failed</literal>: ++		      may be returned if the unenroll failed for a generic reason.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Cancelled</literal>: ++		      returned if the operation was cancelled.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotAuthorized</literal>: ++		      returned if the calling client is not permitted to perform an unenroll ++		      operation.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.AuthenticationFailed</literal>: ++		      returned if the credentials passed did not authenticate against the realm ++		      correctly. It is appropriate to prompt the user again.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.NotEnrolled</literal>: ++		      returned if not enrolled in this realm.</para></listitem> ++		    <listitem><para><literal>org.freedesktop.realmd.Error.Busy</literal>: ++		      returned if the service is currently performing another operation like ++		      join or leave.</para></listitem> ++		  </itemizedlist> ++		--> ++		<method name="Leave"> ++			<arg name="credentials" type="(ssv)" direction="in"/> ++			<arg name="options" type="a{sv}" direction="in"/> ++		</method> ++ ++	</interface> ++ ++</node> +--  +2.27.0 + diff --git a/0001-data-don-t-send-change-updates-for-login-history.patch b/0001-data-don-t-send-change-updates-for-login-history.patch new file mode 100644 index 0000000..94d6c7a --- /dev/null +++ b/0001-data-don-t-send-change-updates-for-login-history.patch @@ -0,0 +1,88 @@ +From 50edc5e45bb984576506e7b2bfb4c267ac566099 Mon Sep 17 00:00:00 2001 +From: Ray Strode <rstrode@redhat.com> +Date: Thu, 9 May 2019 14:58:34 -0400 +Subject: [PATCH] data: don't send change updates for login-history + +The login-history property of user objects can be quite large. +If wtmp is changed frequently, that can lead to memory fragmentation +in clients. + +Furthermore, most clients never check login-history, so it's +wasted memory and wasted cpu. + +This commit disables change notification for that property.  If +a client really needs to get updates, they can manually refresh +their cache when appropriate. +--- + data/org.freedesktop.Accounts.User.xml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml +index 8d3fe1c..3b839a3 100644 +--- a/data/org.freedesktop.Accounts.User.xml ++++ b/data/org.freedesktop.Accounts.User.xml +@@ -785,60 +785,61 @@ +     <doc:doc> +       <doc:description> +         <doc:para> +           The users location. +         </doc:para> +       </doc:description> +     </doc:doc> +   </property> +  +   <property name="LoginFrequency" type="t" access="read"> +     <doc:doc> +       <doc:description> +         <doc:para> +           How often the user has logged in. +         </doc:para> +       </doc:description> +     </doc:doc> +   </property> +  +   <property name="LoginTime" type="x" access="read"> +     <doc:doc> +       <doc:description> +         <doc:para> +           The last login time. +         </doc:para> +       </doc:description> +     </doc:doc> +   </property> +  +   <property name="LoginHistory" type="a(xxa{sv})" access="read"> ++    <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> +     <doc:doc> +       <doc:description> +         <doc:para> +           The login history for this user. +           Each entry in the array represents a login session. The first two +           members are the login time and logout time, as timestamps (seconds since the epoch). If the session is still running, the logout time +           is 0. +         </doc:para> +         <doc:para> +           The a{sv} member is a dictionary containing additional information +           about the session. Possible members include 'type' (with values like ':0', 'tty0', 'pts/0' etc). +         </doc:para> +       </doc:description> +     </doc:doc> +   </property> +  +   <property name="IconFile" type="s" access="read"> +     <doc:doc> +       <doc:description> +         <doc:para> +            The filename of a png file containing the users icon. +         </doc:para> +       </doc:description> +     </doc:doc> +   </property> +  +   <property name="Saved" type="b" access="read"> +     <doc:doc> +       <doc:description> +         <doc:para> +--  +2.27.0 + diff --git a/0001-lib-save-os-when-creating-user.patch b/0001-lib-save-os-when-creating-user.patch new file mode 100644 index 0000000..08d953d --- /dev/null +++ b/0001-lib-save-os-when-creating-user.patch @@ -0,0 +1,894 @@ +From 5124220f12a157ff285072a4f786db2c94c3ca8a Mon Sep 17 00:00:00 2001 +From: Ray Strode <rstrode@redhat.com> +Date: Thu, 14 Jan 2021 15:07:34 -0500 +Subject: [PATCH] lib: save os when creating user + +In order to identify that a user has upgraded from rhel 7 to +rhel 8, we need to know what os they were using when created. + +This commit saves that information using a red hat specific +extension to accountsservice. +--- + .../com.redhat.AccountsServiceUser.System.xml |  10 ++ + data/meson.build                              |   1 + + meson.build                                   |   1 + + meson_post_install.py                         |  15 +++ + src/libaccountsservice/act-user-manager.c     | 123 ++++++++++++++++++ + src/libaccountsservice/meson.build            |   7 + + 6 files changed, 157 insertions(+) + create mode 100644 data/com.redhat.AccountsServiceUser.System.xml + +diff --git a/data/com.redhat.AccountsServiceUser.System.xml b/data/com.redhat.AccountsServiceUser.System.xml +new file mode 100644 +index 0000000..67f5f30 +--- /dev/null ++++ b/data/com.redhat.AccountsServiceUser.System.xml +@@ -0,0 +1,10 @@ ++<node> ++  <interface name="com.redhat.AccountsServiceUser.System"> ++ ++    <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/> ++ ++    <property name="id" type="s" access="readwrite"/> ++    <property name="version-id" type="s" access="readwrite"/> ++ ++  </interface> ++</node> +diff --git a/data/meson.build b/data/meson.build +index 4987937..2dc57c2 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -1,33 +1,34 @@ + ifaces = files( +   act_namespace + '.xml', +   act_namespace + '.User.xml', ++  'com.redhat.AccountsServiceUser.System.xml', + ) +  + install_data( +   ifaces, +   install_dir: dbus_ifaces_dir, + ) +  + install_data( +   act_namespace + '.conf', +   install_dir: dbus_conf_dir, + ) +  + service_conf = configuration_data() + service_conf.set('libexecdir', act_libexecdir) +  + service = act_namespace + '.service' +  + configure_file( +   input: service + '.in', +   output: service, +   configuration: service_conf, +   install: true, +   install_dir: dbus_sys_dir, + ) +  + policy = act_namespace.to_lower() + '.policy' +  + i18n.merge_file( +   policy, +   input: policy + '.in', +diff --git a/meson.build b/meson.build +index 4465a26..e37c451 100644 +--- a/meson.build ++++ b/meson.build +@@ -174,38 +174,39 @@ assert(not enable_systemd or not enable_elogind, 'systemd and elogind support re + if enable_systemd +   logind_dep = dependency('libsystemd', version: '>= 186') + endif +  + if enable_elogind +   logind_dep = dependency('libelogind', version: '>= 229.4') + endif + config_h.set('WITH_SYSTEMD', enable_systemd or enable_elogind) +  + subdir('data') + subdir('src') + subdir('po') +  + enable_docbook = get_option('docbook') + if enable_docbook +   subdir('doc/dbus') + endif +  + if get_option('gtk_doc') +   subdir('doc/libaccountsservice') + endif +  + configure_file( +   output: 'config.h', +   configuration: config_h, + ) +  + meson.add_install_script( +   'meson_post_install.py', +   act_localstatedir, ++  act_datadir, + ) +  + output = '\n' + meson.project_name() + ' was configured with the following options:\n' + output += '** DocBook documentation build: ' + enable_docbook.to_string() + '\n' + output += '** Administrator group: ' + admin_group + '\n' + output += '** Extra administrator groups: ' + extra_admin_groups + '\n' + output += '** GDM configuration: ' + gdm_conf_file + message(output) +diff --git a/meson_post_install.py b/meson_post_install.py +index 5cc2dc4..e1d5a71 100644 +--- a/meson_post_install.py ++++ b/meson_post_install.py +@@ -1,18 +1,33 @@ + #!/usr/bin/env python3 +  + import os + import sys +  + destdir = os.environ.get('DESTDIR', '') + localstatedir = os.path.normpath(destdir + os.sep + sys.argv[1]) ++datadir = os.path.normpath(destdir + os.sep + sys.argv[2]) ++interfacedir = os.path.join(datadir, 'accountsservice', 'interfaces') +  + # FIXME: meson will not track the creation of these directories + #        https://github.com/mesonbuild/meson/blob/master/mesonbuild/scripts/uninstall.py#L39 + dst_dirs = [ +   (os.path.join(localstatedir, 'lib', 'AccountsService', 'icons'), 0o775), +   (os.path.join(localstatedir, 'lib', 'AccountsService', 'users'), 0o700), ++  (interfacedir, 0o775), + ] +  + for (dst_dir, dst_dir_mode) in dst_dirs: +   if not os.path.exists(dst_dir): +     os.makedirs(dst_dir, mode=dst_dir_mode) ++ ++interface_files = [ ++  'com.redhat.AccountsServiceUser.System.xml', ++] ++ ++for interface_file in interface_files: ++    src_path = os.path.join('../../dbus-1/interfaces', interface_file) ++    dst_path = os.path.join(interfacedir, interface_file) ++    if not os.path.exists(dst_path): ++        os.symlink(src_path, dst_path) ++ ++ +diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c +index 1b5298d..f4598c4 100644 +--- a/src/libaccountsservice/act-user-manager.c ++++ b/src/libaccountsservice/act-user-manager.c +@@ -27,60 +27,61 @@ + #include <string.h> + #include <signal.h> + #include <errno.h> + #include <sys/stat.h> + #include <sys/types.h> +  + #ifdef HAVE_PATHS_H + #include <paths.h> + #endif /* HAVE_PATHS_H */ +  + #include <glib.h> + #include <glib/gi18n-lib.h> + #include <glib/gstdio.h> + #include <glib-object.h> + #include <gio/gio.h> + #include <gio/gunixinputstream.h> +  + #ifdef WITH_SYSTEMD + #include <systemd/sd-login.h> +  + /* check if logind is running */ + #define LOGIND_RUNNING() (access("/run/systemd/seats/", F_OK) >= 0) + #endif +  + #include "act-user-manager.h" + #include "act-user-private.h" + #include "accounts-generated.h" + #include "ck-manager-generated.h" + #include "ck-seat-generated.h" + #include "ck-session-generated.h" ++#include "com.redhat.AccountsServiceUser.System.h" +  + /** +  * SECTION:act-user-manager +  * @title: ActUserManager +  * @short_description: manages ActUser objects +  * +  * ActUserManager is a manager object that gives access to user +  * creation, deletion, enumeration, etc. +  * +  * There is typically a singleton ActUserManager object, which +  * can be obtained by act_user_manager_get_default(). +  */ +  + /** +  * ActUserManager: +  * +  * A user manager object. +  */ +  + /** +  * ACT_USER_MANAGER_ERROR: +  * +  * The GError domain for #ActUserManagerError errors +  */ +  + /** +  * ActUserManagerError: +  * @ACT_USER_MANAGER_ERROR_FAILED: Generic failure +  * @ACT_USER_MANAGER_ERROR_USER_EXISTS: The user already exists +  * @ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST: The user does not exist +@@ -165,60 +166,63 @@ typedef struct +         ActUser                    *user; +         ActUserManagerFetchUserRequestType type; +         union { +                 char               *username; +                 uid_t               uid; +         }; +         char                       *object_path; +         char                       *description; + } ActUserManagerFetchUserRequest; +  + typedef struct + { +         GHashTable            *normal_users_by_name; +         GHashTable            *system_users_by_name; +         GHashTable            *users_by_object_path; +         GHashTable            *sessions; +         GDBusConnection       *connection; +         AccountsAccounts      *accounts_proxy; +         ConsoleKitManager     *ck_manager_proxy; +  +         ActUserManagerSeat     seat; +  +         GSList                *new_sessions; +         GSList                *new_users; +         GSList                *new_users_inhibiting_load; +         GSList                *fetch_user_requests; +  +         GSList                *exclude_usernames; +         GSList                *include_usernames; +  ++        char                  *os_id; ++        char                  *os_version_id; ++ +         guint                  load_id; +  +         gboolean               is_loaded; +         gboolean               has_multiple_users; +         gboolean               getting_sessions; +         gboolean               list_cached_users_done; + } ActUserManagerPrivate; +  + enum { +         PROP_0, +         PROP_INCLUDE_USERNAMES_LIST, +         PROP_EXCLUDE_USERNAMES_LIST, +         PROP_IS_LOADED, +         PROP_HAS_MULTIPLE_USERS + }; +  + enum { +         USER_ADDED, +         USER_REMOVED, +         USER_IS_LOGGED_IN_CHANGED, +         USER_CHANGED, +         LAST_SIGNAL + }; +  + static guint signals [LAST_SIGNAL] = { 0, }; +  + static void     act_user_manager_class_init (ActUserManagerClass *klass); + static void     act_user_manager_init       (ActUserManager      *user_manager); + static void     act_user_manager_finalize   (GObject             *object); +  +@@ -2942,100 +2946,173 @@ ensure_accounts_proxy (ActUserManager *manager) +                                                                  G_DBUS_PROXY_FLAGS_NONE, +                                                                  ACCOUNTS_NAME, +                                                                  ACCOUNTS_PATH, +                                                                  NULL, +                                                                  &error); +         if (error != NULL) { +                 g_debug ("ActUserManager: getting account proxy failed: %s", error->message); +                 return FALSE; +         } +  +         g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->accounts_proxy), G_MAXINT); +  +         g_object_bind_property (G_OBJECT (priv->accounts_proxy), +                                 "has-multiple-users", +                                 G_OBJECT (manager), +                                 "has-multiple-users", +                                 G_BINDING_SYNC_CREATE); +  +         g_signal_connect (priv->accounts_proxy, +                           "user-added", +                           G_CALLBACK (on_new_user_in_accounts_service), +                           manager); +         g_signal_connect (priv->accounts_proxy, +                           "user-deleted", +                           G_CALLBACK (on_user_removed_in_accounts_service), +                           manager); +  +         return TRUE; + } +  ++static inline gboolean ++is_valid_char (gchar    c, ++               gboolean first) ++{ ++        return (!first && g_ascii_isdigit (c)) || ++                c == '_' || ++                g_ascii_isalpha (c); ++} ++ ++static void ++load_os_release (ActUserManager *manager) ++{ ++        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); ++        g_autoptr(GFile) file = NULL; ++        g_autoptr(GError) error = NULL; ++        g_autofree char *contents = NULL; ++        g_auto(GStrv) lines = NULL; ++        size_t i; ++ ++        file = g_file_new_for_path ("/etc/os-release"); ++ ++        if (!g_file_load_contents (file, NULL, &contents, NULL, NULL, &error)) { ++                g_debug ("ActUserManager: couldn't load /etc/os-release: %s", error->message); ++                return; ++        } ++ ++        lines = g_strsplit (contents, "\n", -1); ++        for (i = 0; lines[i] != NULL; i++) { ++                char *p, *name, *name_end, *value, *value_end; ++ ++                p = lines[i]; ++ ++                while (g_ascii_isspace (*p)) ++                        p++; ++ ++                if (*p == '#' || *p == '\0') ++                        continue; ++                name = p; ++                while (is_valid_char (*p, p == name)) ++                        p++; ++                name_end = p; ++                while (g_ascii_isspace (*p)) ++                        p++; ++                if (name == name_end || *p != '=') { ++                        continue; ++                } ++                *name_end = '\0'; ++ ++                p++; ++ ++                while (g_ascii_isspace (*p)) ++                        p++; ++ ++                value = p; ++                value_end = value + strlen (value) - 1; ++ ++                if (value != value_end && *value == '"' && *value_end == '"') { ++                        value++; ++                        *value_end = '\0'; ++                } ++ ++                if (strcmp (name, "ID") == 0) { ++                        g_debug ("ActUserManager: system OS is '%s'", value); ++                        priv->os_id = g_strdup (value); ++                } else if (strcmp (name, "VERSION_ID") == 0) { ++                        g_debug ("ActUserManager: system OS version is '%s'", value); ++                        priv->os_version_id = g_strdup (value); ++                } ++        } ++} ++ + static void + act_user_manager_init (ActUserManager *manager) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +         g_autoptr(GError) error = NULL; +  +         act_user_manager_error_quark (); /* register dbus errors */ +  +         /* sessions */ +         priv->sessions = g_hash_table_new_full (g_str_hash, +                                                 g_str_equal, +                                                 g_free, +                                                 g_object_unref); +  +         /* users */ +         priv->normal_users_by_name = g_hash_table_new_full (g_str_hash, +                                                             g_str_equal, +                                                             g_free, +                                                             g_object_unref); +         priv->system_users_by_name = g_hash_table_new_full (g_str_hash, +                                                             g_str_equal, +                                                             g_free, +                                                             g_object_unref); +         priv->users_by_object_path = g_hash_table_new_full (g_str_hash, +                                                             g_str_equal, +                                                             NULL, +                                                             g_object_unref); +  +         priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); +         if (priv->connection == NULL) { +                 if (error != NULL) { +                         g_warning ("Failed to connect to the D-Bus daemon: %s", error->message); +                 } else { +                         g_warning ("Failed to connect to the D-Bus daemon"); +                 } +                 return; +         } +  +         ensure_accounts_proxy (manager); +  ++        load_os_release (manager); ++ +         priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED; + } +  + static void + act_user_manager_finalize (GObject *object) + { +         ActUserManager *manager = ACT_USER_MANAGER (object); +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +         GSList         *node; +  +         g_debug ("ActUserManager: finalizing user manager"); +  +         g_slist_foreach (priv->new_sessions, +                          (GFunc) unload_new_session, NULL); +         g_slist_free (priv->new_sessions); +  +         g_slist_foreach (priv->fetch_user_requests, +                          (GFunc) free_fetch_user_request, NULL); +         g_slist_free (priv->fetch_user_requests); +  +         g_slist_free (priv->new_users_inhibiting_load); +  +         node = priv->new_users; +         while (node != NULL) { +                 ActUser *user; +                 GSList  *next_node; +  +                 user = ACT_USER (node->data); +                 next_node = node->next; +  +@@ -3071,143 +3148,181 @@ act_user_manager_finalize (GObject *object) +  + #ifdef WITH_SYSTEMD +         if (priv->seat.session_monitor != NULL) { +                 sd_login_monitor_unref (priv->seat.session_monitor); +         } +  +         if (priv->seat.session_monitor_stream != NULL) { +                 g_object_unref (priv->seat.session_monitor_stream); +         } +  +         if (priv->seat.session_monitor_source_id != 0) { +                 g_source_remove (priv->seat.session_monitor_source_id); +         } + #endif +  +         if (priv->accounts_proxy != NULL) { +                 g_object_unref (priv->accounts_proxy); +         } +  +         if (priv->load_id > 0) { +                 g_source_remove (priv->load_id); +                 priv->load_id = 0; +         } +  +         g_hash_table_destroy (priv->sessions); +  +         g_hash_table_destroy (priv->normal_users_by_name); +         g_hash_table_destroy (priv->system_users_by_name); +         g_hash_table_destroy (priv->users_by_object_path); +  ++        g_free (priv->os_id); ++        g_free (priv->os_version_id); ++ +         G_OBJECT_CLASS (act_user_manager_parent_class)->finalize (object); + } +  + /** +  * act_user_manager_get_default: +  * +  * Returns the user manager singleton instance.  Calling this function will +  * automatically being loading the user list if it isn't loaded already. +  * The #ActUserManager:is-loaded property will be set to %TRUE when the users +  * are finished loading and then act_user_manager_list_users() can be called. +  * +  * Returns: (transfer none): user manager object +  */ + ActUserManager * + act_user_manager_get_default (void) + { +         if (user_manager_object == NULL) { +                 user_manager_object = g_object_new (ACT_TYPE_USER_MANAGER, NULL); +                 g_object_add_weak_pointer (user_manager_object, +                                            (gpointer *) &user_manager_object); +                 act_user_manager_queue_load (user_manager_object); +         } +  +         return ACT_USER_MANAGER (user_manager_object); + } +  + /** +  * act_user_manager_no_service: +  * @manager: a #ActUserManager +  * +  * Check whether or not the accounts service is running. +  * +  * Returns: whether or not accounts service is running +  */ + gboolean + act_user_manager_no_service (ActUserManager *manager) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +         return priv->accounts_proxy == NULL; + } +  ++static void ++save_system_info (ActUserManager *manager, ++                  const char     *user_path) ++{ ++        ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); ++        ActUserSystem *user_system_proxy = NULL; ++        g_autoptr(GError) error = NULL; ++ ++        if (priv->os_id == NULL && priv->os_version_id == NULL) ++                return; ++ ++        user_system_proxy = act_user_system_proxy_new_sync (priv->connection, ++                                                            G_DBUS_PROXY_FLAGS_NONE, ++                                                            ACCOUNTS_NAME, ++                                                            user_path, ++                                                            NULL, ++                                                            &error); ++        if (user_system_proxy != NULL) { ++                if (priv->os_id != NULL) ++                        act_user_system_set_id (user_system_proxy, priv->os_id); ++ ++                if (priv->os_version_id != NULL) ++                        act_user_system_set_version_id (user_system_proxy, priv->os_version_id); ++        } else { ++                /* probably means accountsservice and lib are out of sync */ ++                g_debug ("ActUserManager: failed to create user system proxy: %s", ++                         error? error->message: ""); ++                g_clear_error (&error); ++        } ++ ++        g_clear_object (&user_system_proxy); ++} ++ + /** +  * act_user_manager_create_user: +  * @manager: a #ActUserManager +  * @username: a unix user name +  * @fullname: a unix GECOS value +  * @accounttype: a #ActUserAccountType +  * @error: a #GError +  * +  * Creates a user account on the system. +  * +  * Returns: (transfer full): user object +  */ + ActUser * + act_user_manager_create_user (ActUserManager      *manager, +                               const char          *username, +                               const char          *fullname, +                               ActUserAccountType   accounttype, +                               GError             **error) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +         GError *local_error = NULL; +         gboolean res; +         g_autofree gchar *path = NULL; +         ActUser *user; +  +         g_debug ("ActUserManager: Creating user '%s', '%s', %d", +                  username, fullname, accounttype); +  +         g_assert (priv->accounts_proxy != NULL); +  +         res = accounts_accounts_call_create_user_sync (priv->accounts_proxy, +                                                        username, +                                                        fullname, +                                                        accounttype, +                                                        &path, +                                                        NULL, +                                                        &local_error); +         if (!res) { +                 g_propagate_error (error, local_error); +                 return NULL; +         } +  ++        save_system_info (manager, path); ++ +         user = add_new_user_for_object_path (path, manager); +  +         return user; + } +  + static void + act_user_manager_async_complete_handler (GObject      *source, +                                          GAsyncResult *result, +                                          gpointer      user_data) + { +         GTask *task = user_data; +  +         g_task_return_pointer (task, g_object_ref (result), g_object_unref); +         g_object_unref (task); + } +  + /** +  * act_user_manager_create_user_async: +  * @manager: a #ActUserManager +  * @username: a unix user name +  * @fullname: a unix GECOS value +  * @accounttype: a #ActUserAccountType +  * @cancellable: (allow-none): optional #GCancellable object, +  *     %NULL to ignore +  * @callback: (scope async): a #GAsyncReadyCallback to call +  *     when the request is satisfied +  * @user_data: (closure): the data to pass to @callback +  * +  * Asynchronously creates a user account on the system. +  * +@@ -3253,106 +3368,111 @@ act_user_manager_create_user_async (ActUserManager      *manager, +  * @manager: a #ActUserManager +  * @result: a #GAsyncResult +  * @error: a #GError +  * +  * Finishes an asynchronous user creation. +  * +  * See act_user_manager_create_user_async(). +  * +  * Returns: (transfer full): user object +  * +  * Since: 0.6.27 +  */ + ActUser * + act_user_manager_create_user_finish (ActUserManager  *manager, +                                      GAsyncResult    *result, +                                      GError         **error) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +         GAsyncResult *inner_result; +         ActUser *user = NULL; +         g_autofree gchar *path = NULL; +         GError *remote_error = NULL; +  +         inner_result = g_task_propagate_pointer (G_TASK (result), error); +         if (inner_result == NULL) { +                 return FALSE; +         } +  +         if (accounts_accounts_call_create_user_finish (priv->accounts_proxy, +                                                        &path, inner_result, &remote_error)) { ++ ++                save_system_info (manager, path); ++ +                 user = add_new_user_for_object_path (path, manager); +         } +  +         if (remote_error) { +                 g_dbus_error_strip_remote_error (remote_error); +                 g_propagate_error (error, remote_error); +         } +  +         return user; + } +  + /** +  * act_user_manager_cache_user: +  * @manager: a #ActUserManager +  * @username: a user name +  * @error: a #GError +  * +  * Caches a user account so it shows up via act_user_manager_list_users(). +  * +  * Returns: (transfer full): user object +  */ + ActUser * + act_user_manager_cache_user (ActUserManager     *manager, +                              const char         *username, +                              GError            **error) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +         GError *local_error = NULL; +         gboolean res; +         g_autofree gchar *path = NULL; +  +         g_debug ("ActUserManager: Caching user '%s'", +                  username); +  +         g_assert (priv->accounts_proxy != NULL); +  +         res = accounts_accounts_call_cache_user_sync (priv->accounts_proxy, +                                                       username, +                                                       &path, +                                                       NULL, +                                                       &local_error); +         if (!res) { +                 g_propagate_error (error, local_error); +                 return NULL; +         } +  ++        save_system_info (manager, path); ++ +         return add_new_user_for_object_path (path, manager); + } +  +  + /** +  * act_user_manager_cache_user_async: +  * @manager: a #ActUserManager +  * @username: a unix user name +  * @cancellable: (allow-none): optional #GCancellable object, +  *     %NULL to ignore +  * @callback: (scope async): a #GAsyncReadyCallback to call +  *     when the request is satisfied +  * @user_data: (closure): the data to pass to @callback +  * +  * Asynchronously caches a user account so it shows up via +  * act_user_manager_list_users(). +  * +  * For more details, see act_user_manager_cache_user(), which +  * is the synchronous version of this call. +  * +  * Since: 0.6.27 +  */ + void + act_user_manager_cache_user_async (ActUserManager      *manager, +                                    const char          *username, +                                    GCancellable        *cancellable, +                                    GAsyncReadyCallback  callback, +                                    gpointer             user_data) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +@@ -3378,60 +3498,63 @@ act_user_manager_cache_user_async (ActUserManager      *manager, +  * @manager: a #ActUserManager +  * @result: a #GAsyncResult +  * @error: a #GError +  * +  * Finishes an asynchronous user caching. +  * +  * See act_user_manager_cache_user_async(). +  * +  * Returns: (transfer full): user object +  * +  * Since: 0.6.27 +  */ + ActUser * + act_user_manager_cache_user_finish (ActUserManager  *manager, +                                     GAsyncResult    *result, +                                     GError         **error) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +         GAsyncResult *inner_result; +         ActUser *user = NULL; +         g_autofree gchar *path = NULL; +         GError *remote_error = NULL; +  +         inner_result = g_task_propagate_pointer (G_TASK (result), error); +         if (inner_result == NULL) { +                 return FALSE; +         } +  +         if (accounts_accounts_call_cache_user_finish (priv->accounts_proxy, +                                                       &path, inner_result, &remote_error)) { ++ ++                save_system_info (manager, path); ++ +                 user = add_new_user_for_object_path (path, manager); +         } +  +         if (remote_error) { +                 g_dbus_error_strip_remote_error (remote_error); +                 g_propagate_error (error, remote_error); +         } +  +         return user; + } +  + /** +  * act_user_manager_uncache_user: +  * @manager: a #ActUserManager +  * @username: a user name +  * @error: a #GError +  * +  * Releases all metadata about a user account, including icon, +  * language and session. If the user account is from a remote +  * server and the user has never logged in before, then that +  * account will no longer show up in ListCachedUsers() output. +  * +  * Returns: %TRUE if successful, otherwise %FALSE +  */ + gboolean + act_user_manager_uncache_user (ActUserManager     *manager, +                                const char         *username, +                                GError            **error) + { +         ActUserManagerPrivate *priv = act_user_manager_get_instance_private (manager); +diff --git a/src/libaccountsservice/meson.build b/src/libaccountsservice/meson.build +index 4e134db..9e85bba 100644 +--- a/src/libaccountsservice/meson.build ++++ b/src/libaccountsservice/meson.build +@@ -21,60 +21,67 @@ enum_types = 'act-user-enum-types' +  + enum_sources = gnome.mkenums( +   enum_types, +   sources: headers, +   c_template: enum_types + '.c.template', +   h_template: enum_types + '.h.template', +   install_header: true, +   install_dir: join_paths(act_pkgincludedir, subdir), + ) +  + dbus_sources = [] +  + ifaces = [ +   'Manager', +   'Seat', +   'Session', + ] +  + namespace = 'ConsoleKit' + prefix = 'org.freedesktop.' + namespace +  + foreach iface: ifaces +   dbus_sources += gnome.gdbus_codegen( +     'ck-@0@-generated'.format(iface.to_lower()), +     '@0@.@1@.xml'.format(prefix, iface), +     interface_prefix: prefix, +     namespace: namespace, +   ) + endforeach +  ++dbus_sources += gnome.gdbus_codegen( ++   'com.redhat.AccountsServiceUser.System', ++   join_paths(data_dir, 'com.redhat.AccountsServiceUser.System.xml'), ++   interface_prefix: 'com.redhat.AccountsService', ++   namespace: 'Act', ++) ++ + deps = [ +   crypt_dep, +   gio_unix_dep, +   glib_dep, +   libaccounts_generated_dep, + ] +  + symbol_map = join_paths(meson.current_source_dir(), 'symbol.map') + ldflags = cc.get_supported_link_arguments('-Wl,--version-script,@0@'.format(symbol_map)) +  + if enable_systemd or enable_elogind +   deps += logind_dep + endif +  + libaccountsservice = shared_library( +   act_name, +   sources: sources + enum_sources + dbus_sources, +   version: libversion, +   include_directories: top_inc, +   dependencies: deps, +   c_args: '-DG_LOG_DOMAIN="@0@"'.format(meson.project_name()), +   link_args: ldflags, +   link_depends: symbol_map, +   install: true, + ) +  + libaccountsservice_dep = declare_dependency( +   sources: enum_sources[1], +   include_directories: include_directories('.'), +   dependencies: [gio_dep, glib_dep], +--  +2.27.0 + diff --git a/0001-user-Introduce-user-templates-for-setting-default-se.patch b/0001-user-Introduce-user-templates-for-setting-default-se.patch new file mode 100644 index 0000000..001de67 --- /dev/null +++ b/0001-user-Introduce-user-templates-for-setting-default-se.patch @@ -0,0 +1,925 @@ +From 72427bd4fcae931298c670093f9cbd34ad58f59a Mon Sep 17 00:00:00 2001 +From: Ray Strode <rstrode@redhat.com> +Date: Wed, 4 Aug 2021 19:54:59 -0400 +Subject: [PATCH] user: Introduce user templates for setting default session + etc + +At the moment there's no easy way to set a default session, or +face icon or whatever for all users.  If a user has never logged in +before, we just generate their cache file from hardcoded defaults. + +This commit introduces a template system to make it possible for +admins to set up defaults on their own. + +Admins can write either +/etc/accountsservice/user-templates/administrator +or +/etc/accountsservice/user-templates/standard + +files.  These files follow the same format as + +/var/lib/AccountsService/users/username + +files, but will support substituting $HOME and $USER to the appropriate +user specific values. + +User templates also support an additional group [Template] that +have an additional key EnvironmentFiles that specify a list +of environment files to load (files with KEY=VALUE pairs in them). +Any keys listed in those environment files will also get substituted. +--- + data/administrator |   6 + + data/meson.build   |  10 ++ + data/standard      |   6 + + src/daemon.c       |   8 +- + src/meson.build    |   1 + + src/user.c         | 284 ++++++++++++++++++++++++++++++++++++++++++++- + src/user.h         |   3 +- + 7 files changed, 305 insertions(+), 13 deletions(-) + create mode 100644 data/administrator + create mode 100644 data/standard + +diff --git a/data/administrator b/data/administrator +new file mode 100644 +index 0000000..ea043c9 +--- /dev/null ++++ b/data/administrator +@@ -0,0 +1,6 @@ ++[Template] ++#EnvironmentFiles=/etc/os-release; ++ ++[User] ++Session= ++Icon=${HOME}/.face +diff --git a/data/meson.build b/data/meson.build +index 2dc57c2..7d9bdcd 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -22,30 +22,40 @@ service = act_namespace + '.service' + configure_file( +   input: service + '.in', +   output: service, +   configuration: service_conf, +   install: true, +   install_dir: dbus_sys_dir, + ) +  + policy = act_namespace.to_lower() + '.policy' +  + i18n.merge_file( +   policy, +   input: policy + '.in', +   output: policy, +   po_dir: po_dir, +   install: true, +   install_dir: policy_dir, + ) +  + if install_systemd_unit_dir +   service = 'accounts-daemon.service' +  +   configure_file( +     input: service + '.in', +     output: service, +     configuration: service_conf, +     install: true, +     install_dir: systemd_system_unit_dir, +   ) + endif ++ ++install_data( ++  'administrator', ++  install_dir: join_paths(act_datadir, 'accountsservice', 'user-templates'), ++) ++ ++install_data( ++  'standard', ++  install_dir: join_paths(act_datadir, 'accountsservice', 'user-templates'), ++) +diff --git a/data/standard b/data/standard +new file mode 100644 +index 0000000..ea043c9 +--- /dev/null ++++ b/data/standard +@@ -0,0 +1,6 @@ ++[Template] ++#EnvironmentFiles=/etc/os-release; ++ ++[User] ++Session= ++Icon=${HOME}/.face +diff --git a/src/daemon.c b/src/daemon.c +index 5ce0216..66ac7ba 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -298,69 +298,63 @@ entry_generator_cachedir (Daemon       *daemon, +                         break; +  +                 /* Only load files in this directory */ +                 filename = g_build_filename (USERDIR, name, NULL); +                 regular = g_file_test (filename, G_FILE_TEST_IS_REGULAR); +  +                 if (regular) { +                         errno = 0; +                         pwent = getpwnam (name); +                         if (pwent != NULL) { +                                 *shadow_entry = getspnam (pwent->pw_name); +  +                                 return pwent; +                         } else if (errno == 0) { +                                 g_debug ("user '%s' in cache dir but not present on system, removing", name); +                                 remove_cache_files (name); +                         } +                         else { +                                 g_warning ("failed to check if user '%s' in cache dir is present on system: %s", +                                   name, g_strerror (errno)); +                         } +                 } +         } +  +         /* Last iteration */ +         g_dir_close (dir); +  +         /* Update all the users from the files in the cache dir */ +         g_hash_table_iter_init (&iter, users); +         while (g_hash_table_iter_next (&iter, &key, &value)) { +-                const gchar *name = key; +                 User *user = value; +-                g_autofree gchar *filename = NULL; +-                g_autoptr(GKeyFile) key_file = NULL; +  +-                filename = g_build_filename (USERDIR, name, NULL); +-                key_file = g_key_file_new (); +-                if (g_key_file_load_from_file (key_file, filename, 0, NULL)) +-                        user_update_from_keyfile (user, key_file); ++                user_update_from_cache (user); +         } +  +         *state = NULL; +         return NULL; + } +  + static struct passwd * + entry_generator_requested_users (Daemon       *daemon, +                                  GHashTable   *users, +                                  gpointer     *state, +                                  struct spwd **shadow_entry) + { +         DaemonPrivate *priv = daemon_get_instance_private (daemon); +         struct passwd *pwent; +         GList *node; +  +         /* First iteration */ +         if (*state == NULL) { +                 *state = priv->explicitly_requested_users; +         } +  +         /* Every iteration */ +  +         if (g_hash_table_size (users) < MAX_LOCAL_USERS) { +                 node = *state; +                 while (node != NULL) { +                         const char *name; +  +                         name = node->data; +                         node = node->next; +diff --git a/src/meson.build b/src/meson.build +index 3970749..d3b0cb9 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -1,59 +1,60 @@ + sources = [] +  + gdbus_headers = [] +  + ifaces = [ +   ['accounts-generated', 'org.freedesktop.', 'Accounts'], +   ['accounts-user-generated', act_namespace + '.', 'User'], +   ['realmd-generated', 'org.freedesktop.', 'realmd'], + ] +  + foreach iface: ifaces +   gdbus_sources = gnome.gdbus_codegen( +     iface[0], +     join_paths(data_dir, iface[1] + iface[2] + '.xml'), +     interface_prefix: iface[1], +     namespace: 'Accounts', +   ) +   sources += gdbus_sources +   gdbus_headers += gdbus_sources[1] + endforeach +  + deps = [ +   gio_dep, +   gio_unix_dep, + ] +  + cflags = [ +   '-DLOCALSTATEDIR="@0@"'.format(act_localstatedir), +   '-DDATADIR="@0@"'.format(act_datadir), ++  '-DSYSCONFDIR="@0@"'.format(act_sysconfdir), +   '-DICONDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'icons')), +   '-DUSERDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'users')), + ] +  + libaccounts_generated = static_library( +   'accounts-generated', +   sources: sources, +   include_directories: top_inc, +   dependencies: deps, +   c_args: cflags, + ) +  + libaccounts_generated_dep = declare_dependency( +   sources: gdbus_headers, +   include_directories: include_directories('.'), +   dependencies: gio_dep, +   link_with: libaccounts_generated, + ) +  + sources = files( +   'daemon.c', +   'extensions.c', +   'main.c', +   'user.c', +   'user-classify.c', +   'util.c', +   'wtmp-helper.c', + ) +  + deps = [ +diff --git a/src/user.c b/src/user.c +index 9f57af5..16c7721 100644 +--- a/src/user.c ++++ b/src/user.c +@@ -43,127 +43,384 @@ + #include <polkit/polkit.h> +  + #include "user-classify.h" + #include "daemon.h" + #include "user.h" + #include "accounts-user-generated.h" + #include "util.h" +  + struct User { +         AccountsUserSkeleton parent; +  +         GDBusConnection *system_bus_connection; +         gchar *object_path; +  +         Daemon       *daemon; +  +         GKeyFile     *keyfile; +  +         gid_t         gid; +         gint64        expiration_time; +         gint64        last_change_time; +         gint64        min_days_between_changes; +         gint64        max_days_between_changes; +         gint64        days_to_warn; +         gint64        days_after_expiration_until_lock; +         GVariant     *login_history; +         gchar        *icon_file; +         gchar        *default_icon_file; +         gboolean      account_expiration_policy_known; +         gboolean      cached; ++        gboolean      template_loaded; +  +         guint        *extension_ids; +         guint         n_extension_ids; +  +         guint         changed_timeout_id; + }; +  + typedef struct UserClass + { +         AccountsUserSkeletonClass parent_class; + } UserClass; +  + static void user_accounts_user_iface_init (AccountsUserIface *iface); ++static void user_update_from_keyfile (User *user, GKeyFile *keyfile); +  + G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init)); +  + static gint + account_type_from_pwent (struct passwd *pwent) + { +         struct group *grp; +         gint i; +  +         if (pwent->pw_uid == 0) { +                 g_debug ("user is root so account type is administrator"); +                 return ACCOUNT_TYPE_ADMINISTRATOR; +         } +  +         grp = getgrnam (ADMIN_GROUP); +         if (grp == NULL) { +                 g_debug (ADMIN_GROUP " group not found"); +                 return ACCOUNT_TYPE_STANDARD; +         } +  +         for (i = 0; grp->gr_mem[i] != NULL; i++) { +                 if (g_strcmp0 (grp->gr_mem[i], pwent->pw_name) == 0) { +                         return ACCOUNT_TYPE_ADMINISTRATOR; +                 } +         } +  +         return ACCOUNT_TYPE_STANDARD; + } +  + static void + user_reset_icon_file (User *user) + { +         const char *icon_file; +         gboolean    icon_is_default; +         const char *home_dir; +  +         icon_file = accounts_user_get_icon_file (ACCOUNTS_USER (user)); +  +         if (icon_file == NULL || g_strcmp0 (icon_file, user->default_icon_file) == 0) { +                 icon_is_default = TRUE; +         } else { +                 icon_is_default = FALSE; +         } +  +         g_free (user->default_icon_file); +         home_dir = accounts_user_get_home_directory (ACCOUNTS_USER (user)); +  +         user->default_icon_file = g_build_filename (home_dir, ".face", NULL); +  +         if (icon_is_default) { +                 accounts_user_set_icon_file (ACCOUNTS_USER (user), user->default_icon_file); +         } + } +  ++static gboolean ++user_has_cache_file (User *user) ++{ ++        g_autofree char *filename = NULL; ++ ++        filename = g_build_filename (USERDIR, user_get_user_name (user), NULL); ++ ++        return g_file_test (filename, G_FILE_TEST_EXISTS); ++} ++ ++static gboolean ++is_valid_shell_identifier_character (char     c, ++                                     gboolean first) ++{ ++        return (!first && g_ascii_isdigit (c)) || ++                c == '_' || ++                g_ascii_isalpha (c); ++} ++ ++static char * ++expand_template_variables (User       *user, ++                           GHashTable *template_variables, ++                           const char *str) ++{ ++        GString *s = g_string_new (""); ++        const char *p, *start; ++        char c; ++ ++        p = str; ++        while (*p) { ++                c = *p; ++                if (c == '\\') { ++                        p++; ++                        c = *p; ++                        if (c != '\0') { ++                                p++; ++                                switch (c) { ++                                case '\\': ++                                        g_string_append_c (s, '\\'); ++                                        break; ++                                case '$': ++                                        g_string_append_c (s, '$'); ++                                        break; ++                                default: ++                                        g_string_append_c (s, '\\'); ++                                        g_string_append_c (s, c); ++                                        break; ++                                } ++                        } ++                } else if (c == '$') { ++                        gboolean brackets = FALSE; ++                        p++; ++                        if (*p == '{') { ++                                brackets = TRUE; ++                                p++; ++                        } ++                        start = p; ++                        while (*p != '\0' && ++                               is_valid_shell_identifier_character (*p, p == start)) ++                                p++; ++                        if (p == start || (brackets && *p != '}')) { ++                                g_string_append_c (s, '$'); ++                                if (brackets) ++                                        g_string_append_c (s, '{'); ++                                g_string_append_len (s, start, p - start); ++                        } else { ++                                g_autofree char *variable = NULL; ++                                const char *value; ++ ++                                if (brackets && *p == '}') ++                                        p++; ++ ++                                variable = g_strndup (start, p - start - 1); ++ ++                                value = g_hash_table_lookup (template_variables, variable); ++                                if (value) { ++                                        g_string_append (s, value); ++                                } ++                        } ++                } else { ++                        p++; ++                        g_string_append_c (s, c); ++                } ++        } ++        return g_string_free (s, FALSE); ++} ++ ++static void ++load_template_environment_file (User       *user, ++                                GHashTable *variables, ++                                const char *file) ++{ ++        g_autofree char *contents = NULL; ++        g_auto (GStrv) lines = NULL; ++        g_autoptr (GError) error = NULL; ++        gboolean file_loaded; ++        size_t i; ++ ++        file_loaded = g_file_get_contents (file, &contents, NULL, &error); ++ ++        if (!file_loaded) { ++                g_debug ("Couldn't load template environment file %s: %s", ++                         file, error->message); ++                return; ++        } ++ ++        lines = g_strsplit (contents, "\n", -1); ++ ++        for (i = 0; lines[i] != NULL; i++) { ++                char *p; ++                char *variable_end; ++                const char *variable; ++                const char *value; ++ ++                p = lines[i]; ++                while (g_ascii_isspace (*p)) ++                        p++; ++                if (*p == '#' || *p == '\0') ++                        continue; ++                variable = p; ++                while (is_valid_shell_identifier_character (*p, p == variable)) ++                        p++; ++                variable_end = p; ++                while (g_ascii_isspace (*p)) ++                        p++; ++                if (variable_end == variable || *p != '=') { ++                        g_debug ("template environment file %s has invalid line '%s'\n", file, lines[i]); ++                        continue; ++                } ++                *variable_end = '\0'; ++                p++; ++                while (g_ascii_isspace (*p)) ++                        p++; ++                value = p; ++ ++                if (g_hash_table_lookup (variables, variable) == NULL) { ++                        g_hash_table_insert (variables, ++                                             g_strdup (variable), ++                                             g_strdup (value)); ++                } ++ ++        } ++} ++ ++static void ++initialize_template_environment (User               *user, ++                                 GHashTable         *variables, ++                                 const char * const *files) ++{ ++        size_t i; ++ ++        g_hash_table_insert (variables, g_strdup ("HOME"), g_strdup (accounts_user_get_home_directory (ACCOUNTS_USER (user)))); ++        g_hash_table_insert (variables, g_strdup ("USER"), g_strdup (user_get_user_name (user))); ++ ++        if (files == NULL) ++                return; ++ ++        for (i = 0; files[i] != NULL; i++) { ++                load_template_environment_file (user, variables, files[i]); ++        } ++} ++ ++static void ++user_update_from_template (User *user) ++{ ++        g_autofree char *filename = NULL; ++        g_autoptr (GKeyFile) key_file = NULL; ++        g_autoptr (GError) error = NULL; ++        g_autoptr (GHashTable) template_variables = NULL; ++        g_auto (GStrv) template_environment_files = NULL; ++        gboolean key_file_loaded = FALSE; ++        const char * const *system_dirs[] = { ++                (const char *[]) { "/run", SYSCONFDIR, NULL }, ++                g_get_system_data_dirs (), ++                NULL ++        }; ++        g_autoptr (GPtrArray) dirs = NULL; ++        AccountType account_type; ++        const char *account_type_string; ++        size_t i, j; ++        g_autofree char *contents = NULL; ++        g_autofree char *expanded = NULL; ++        g_auto (GStrv) lines = NULL; ++ ++        if (user->template_loaded) ++                return; ++ ++        filename = g_build_filename (USERDIR, ++                                     accounts_user_get_user_name (ACCOUNTS_USER (user)), ++                                     NULL); ++ ++        account_type = accounts_user_get_account_type (ACCOUNTS_USER (user)); ++        if (account_type == ACCOUNT_TYPE_ADMINISTRATOR) ++                account_type_string = "administrator"; ++        else ++                account_type_string = "standard"; ++ ++        dirs = g_ptr_array_new (); ++        for (i = 0; system_dirs[i] != NULL; i++) { ++                for (j = 0; system_dirs[i][j] != NULL; j++) { ++                        char *dir; ++ ++                        dir = g_build_filename (system_dirs[i][j], ++                                                "accountsservice", ++                                                "user-templates", ++                                                NULL); ++                        g_ptr_array_add (dirs, dir); ++                } ++        } ++        g_ptr_array_add (dirs, NULL); ++ ++        key_file = g_key_file_new (); ++        key_file_loaded = g_key_file_load_from_dirs (key_file, ++                                                     account_type_string, ++                                                     (const char **) dirs->pdata, ++                                                     NULL, ++                                                     G_KEY_FILE_KEEP_COMMENTS, ++                                                     &error); ++ ++        if (!key_file_loaded) { ++                g_debug ("failed to load user template: %s", error->message); ++                return; ++        } ++ ++        template_variables = g_hash_table_new_full (g_str_hash, ++                                                    g_str_equal, ++                                                    g_free, ++                                                    g_free); ++ ++        template_environment_files = g_key_file_get_string_list (key_file, ++                                                                 "Template", ++                                                                 "EnvironmentFiles", ++                                                                 NULL, ++                                                                 NULL); ++ ++        initialize_template_environment (user, template_variables, (const char * const *) template_environment_files); ++ ++        g_key_file_remove_group (key_file, "Template", NULL); ++        contents = g_key_file_to_data (key_file, NULL, NULL); ++        lines = g_strsplit (contents, "\n", -1); ++ ++        expanded = expand_template_variables (user, template_variables, contents); ++ ++        key_file_loaded = g_key_file_load_from_data (key_file, ++                                                     expanded, ++                                                     strlen (expanded), ++                                                     G_KEY_FILE_KEEP_COMMENTS, ++                                                     &error); ++ ++        if (key_file_loaded) ++                user_update_from_keyfile (user, key_file); ++ ++        user->template_loaded = key_file_loaded; ++} ++ + void + user_update_from_pwent (User          *user, +                         struct passwd *pwent, +                         struct spwd   *spent) + { +         g_autofree gchar *real_name = NULL; +         gboolean is_system_account; +         const gchar *passwd; +         gboolean locked; +         PasswordMode mode; +         AccountType account_type; +  +         g_object_freeze_notify (G_OBJECT (user)); +  +         if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') { +                 gchar *first_comma = NULL; +                 gchar *valid_utf8_name = NULL; +  +                 if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) { +                         valid_utf8_name = pwent->pw_gecos; +                         first_comma = g_utf8_strchr (valid_utf8_name, -1, ','); +                 } +                 else { +                         g_warning ("User %s has invalid UTF-8 in GECOS field. " +                                    "It would be a good thing to check /etc/passwd.", +                                    pwent->pw_name ? pwent->pw_name : ""); +                 } +  +                 if (first_comma) { +                         real_name = g_strndup (valid_utf8_name, +@@ -212,134 +469,150 @@ user_update_from_pwent (User          *user, +         accounts_user_set_locked (ACCOUNTS_USER (user), locked); +  +         if (passwd == NULL || passwd[0] != 0) { +                 mode = PASSWORD_MODE_REGULAR; +         } +         else { +                 mode = PASSWORD_MODE_NONE; +         } +  +         if (spent) { +                 if (spent->sp_lstchg == 0) { +                         mode = PASSWORD_MODE_SET_AT_LOGIN; +                 } +  +                 user->expiration_time = spent->sp_expire; +                 user->last_change_time  = spent->sp_lstchg; +                 user->min_days_between_changes = spent->sp_min; +                 user->max_days_between_changes = spent->sp_max; +                 user->days_to_warn  = spent->sp_warn; +                 user->days_after_expiration_until_lock = spent->sp_inact; +                 user->account_expiration_policy_known = TRUE; +         } +  +         accounts_user_set_password_mode (ACCOUNTS_USER (user), mode); +         is_system_account = !user_classify_is_human (accounts_user_get_uid (ACCOUNTS_USER (user)), +                                                      accounts_user_get_user_name (ACCOUNTS_USER (user)), +                                                      accounts_user_get_shell (ACCOUNTS_USER (user)), +                                                      passwd); +         accounts_user_set_system_account (ACCOUNTS_USER (user), is_system_account); +  ++        if (!user_has_cache_file (user)) ++                user_update_from_template (user); +         g_object_thaw_notify (G_OBJECT (user)); + } +  +-void ++static void + user_update_from_keyfile (User     *user, +                           GKeyFile *keyfile) + { +         gchar *s; +  +-        g_object_freeze_notify (G_OBJECT (user)); +- +         s = g_key_file_get_string (keyfile, "User", "Language", NULL); +         if (s != NULL) { +                 accounts_user_set_language (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         s = g_key_file_get_string (keyfile, "User", "XSession", NULL); +         if (s != NULL) { +                 accounts_user_set_xsession (ACCOUNTS_USER (user), s); +  +                 /* for backward compat */ +                 accounts_user_set_session (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         s = g_key_file_get_string (keyfile, "User", "Session", NULL); +         if (s != NULL) { +                 accounts_user_set_session (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         s = g_key_file_get_string (keyfile, "User", "SessionType", NULL); +         if (s != NULL) { +                 accounts_user_set_session_type (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         s = g_key_file_get_string (keyfile, "User", "Email", NULL); +         if (s != NULL) { +                 accounts_user_set_email (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         s = g_key_file_get_string (keyfile, "User", "Location", NULL); +         if (s != NULL) { +                 accounts_user_set_location (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL); +         if (s != NULL) { +                 accounts_user_set_password_hint (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         s = g_key_file_get_string (keyfile, "User", "Icon", NULL); +         if (s != NULL) { +                 accounts_user_set_icon_file (ACCOUNTS_USER (user), s); +                 g_clear_pointer (&s, g_free); +         } +  +         if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) { +             gboolean system_account; +  +             system_account = g_key_file_get_boolean (keyfile, "User", "SystemAccount", NULL); +             accounts_user_set_system_account (ACCOUNTS_USER (user), system_account); +         } +  +         g_clear_pointer (&user->keyfile, g_key_file_unref); +         user->keyfile = g_key_file_ref (keyfile); ++} ++ ++void ++user_update_from_cache (User *user) ++{ ++        g_autofree gchar *filename = NULL; ++        g_autoptr(GKeyFile) key_file = NULL; ++ ++        filename = g_build_filename (USERDIR, accounts_user_get_user_name (ACCOUNTS_USER (user)), NULL); ++ ++        key_file = g_key_file_new (); ++ ++        if (!g_key_file_load_from_file (key_file, filename, 0, NULL)) ++                return; ++ ++        g_object_freeze_notify (G_OBJECT (user)); ++        user_update_from_keyfile (user, key_file); +         user_set_cached (user, TRUE); +         user_set_saved (user, TRUE); +- +         g_object_thaw_notify (G_OBJECT (user)); + } +  + void + user_update_local_account_property (User          *user, +                                     gboolean       local) + { +         accounts_user_set_local_account (ACCOUNTS_USER (user), local); + } +  + void + user_update_system_account_property (User          *user, +                                      gboolean       system) + { +         accounts_user_set_system_account (ACCOUNTS_USER (user), system); + } +  + static void + user_save_to_keyfile (User     *user, +                       GKeyFile *keyfile) + { +         g_key_file_remove_group (keyfile, "User", NULL); +  +         if (accounts_user_get_email (ACCOUNTS_USER (user))) +                 g_key_file_set_string (keyfile, "User", "Email", accounts_user_get_email (ACCOUNTS_USER (user))); +  +         if (accounts_user_get_language (ACCOUNTS_USER (user))) +                 g_key_file_set_string (keyfile, "User", "Language", accounts_user_get_language (ACCOUNTS_USER (user))); +  +         if (accounts_user_get_session (ACCOUNTS_USER (user))) +@@ -509,60 +782,63 @@ user_extension_set_property (User                  *user, +         if (!prev || !g_str_equal (printed, prev)) { +                 g_key_file_set_value (user->keyfile, interface->name, property->name, printed); +  +                 /* Emit a change signal.  Use invalidation +                  * because the data may not be world-readable. +                  */ +                 g_dbus_connection_emit_signal (g_dbus_method_invocation_get_connection (invocation), +                                                NULL, /* destination_bus_name */ +                                                g_dbus_method_invocation_get_object_path (invocation), +                                                "org.freedesktop.DBus.Properties", "PropertiesChanged", +                                                g_variant_new_parsed ("( %s, %a{sv}, [ %s ] )", +                                                                      interface->name, NULL, property->name), +                                                NULL); +  +                 accounts_user_emit_changed (ACCOUNTS_USER (user)); +                 save_extra_data (user); +         } +  +         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); + } +  + static void + user_extension_authentication_done (Daemon                *daemon, +                                     User                  *user, +                                     GDBusMethodInvocation *invocation, +                                     gpointer               user_data) + { +         GDBusInterfaceInfo *interface = user_data; +         const gchar *method_name; +  ++        if (!user_has_cache_file (user)) ++                user_update_from_template (user); ++ +         method_name = g_dbus_method_invocation_get_method_name (invocation); +  +         if (g_str_equal (method_name, "Get")) +                 user_extension_get_property (user, daemon, interface, invocation); +         else if (g_str_equal (method_name, "GetAll")) +                 user_extension_get_all_properties (user, daemon, interface, invocation); +         else if (g_str_equal (method_name, "Set")) +                 user_extension_set_property (user, daemon, interface, invocation); +         else +                 g_assert_not_reached (); + } +  + static void + user_extension_method_call (GDBusConnection       *connection, +                             const gchar           *sender, +                             const gchar           *object_path, +                             const gchar           *interface_name, +                             const gchar           *method_name, +                             GVariant              *parameters, +                             GDBusMethodInvocation *invocation, +                             gpointer               user_data) + { +         User *user = user_data; +         GDBusInterfaceInfo *iface_info; +         const gchar *annotation_name; +         const gchar *action_id; +         gint uid; +         gint i; +  +         /* We don't allow method calls on extension interfaces, so we +diff --git a/src/user.h b/src/user.h +index b3b3380..eb81918 100644 +--- a/src/user.h ++++ b/src/user.h +@@ -30,58 +30,57 @@ + #include "types.h" +  + G_BEGIN_DECLS +  + #define TYPE_USER (user_get_type ()) + #define USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_USER, User)) + #define IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_USER)) +  + typedef enum { +         ACCOUNT_TYPE_STANDARD, +         ACCOUNT_TYPE_ADMINISTRATOR, + #define ACCOUNT_TYPE_LAST ACCOUNT_TYPE_ADMINISTRATOR + } AccountType; +  + typedef enum { +         PASSWORD_MODE_REGULAR, +         PASSWORD_MODE_SET_AT_LOGIN, +         PASSWORD_MODE_NONE, + #define PASSWORD_MODE_LAST PASSWORD_MODE_NONE + } PasswordMode; +  + /* local methods */ +  + GType          user_get_type                (void) G_GNUC_CONST; + User *         user_new                     (Daemon        *daemon, +                                              uid_t          uid); +  + void           user_update_from_pwent       (User          *user, +                                              struct passwd *pwent, +                                              struct spwd   *spent); +-void           user_update_from_keyfile     (User          *user, +-                                             GKeyFile      *keyfile); ++void           user_update_from_cache       (User *user); + void           user_update_local_account_property (User          *user, +                                                    gboolean       local); + void           user_update_system_account_property (User          *user, +                                                     gboolean       system); + gboolean       user_get_cached              (User          *user); + void           user_set_cached              (User          *user, +                                              gboolean       cached); + void           user_set_saved               (User          *user, +                                              gboolean       saved); +  + void           user_register                (User          *user); + void           user_unregister              (User          *user); + void           user_changed                 (User          *user); +  + void           user_save                    (User          *user); +  + const gchar *  user_get_user_name           (User          *user); + gboolean       user_get_system_account      (User          *user); + 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); +  + G_END_DECLS +  + #endif +--  +2.27.0 + diff --git a/0002-main-Allow-cache-files-to-be-marked-immutable.patch b/0002-main-Allow-cache-files-to-be-marked-immutable.patch new file mode 100644 index 0000000..991a879 --- /dev/null +++ b/0002-main-Allow-cache-files-to-be-marked-immutable.patch @@ -0,0 +1,195 @@ +From 12127d9c04e8151c51bd14114dce424ff8448345 Mon Sep 17 00:00:00 2001 +From: Ray Strode <rstrode@redhat.com> +Date: Thu, 9 Sep 2021 09:40:49 -0400 +Subject: [PATCH 2/2] main: Allow cache files to be marked immutable + +At the moment, at start up we unconditionally reset permission of all +cache files in /var/lib/AccountsService/users.  If the mode of the files +can't be reset, accountsservice fails to start. + +But there's a situation where we should proceed anyway: If the +mode is already correct, and the file is read-only, there is no reason +to refuse to proceed. + +This commit changes the code to explicitly validate the permissions of +the file before failing. +--- + src/main.c | 29 +++++++++++++++++++++++++---- + 1 file changed, 25 insertions(+), 4 deletions(-) + +diff --git a/src/main.c b/src/main.c +index 01cb617..36a2d7e 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -16,143 +16,164 @@ +  * along with this program; if not, write to the Free Software +  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +  * +  * Written by: Matthias Clasen <mclasen@redhat.com> +  */ +  + #include "config.h" +  + #include <stdlib.h> + #include <stdarg.h> + #include <locale.h> + #include <libintl.h> + #include <syslog.h> + #include <sys/stat.h> + #include <errno.h> +  + #include <glib.h> + #include <glib/gi18n.h> + #include <glib/gstdio.h> + #include <glib-unix.h> +  + #include "daemon.h" +  + #define NAME_TO_CLAIM "org.freedesktop.Accounts" +  + static gboolean + ensure_directory (const char  *path, +                   gint         mode, +                   GError     **error) + { ++        GStatBuf stat_buffer = { 0 }; ++ +         if (g_mkdir_with_parents (path, mode) < 0) { +                 g_set_error (error, +                              G_FILE_ERROR, +                              g_file_error_from_errno (errno), +                              "Failed to create directory %s: %m", +                              path); +                 return FALSE; +         } +  +-        if (g_chmod (path, mode) < 0) { ++        g_chmod (path, mode); ++ ++        if (g_stat (path, &stat_buffer) < 0) { ++                g_clear_error (error); ++ +                 g_set_error (error, +                              G_FILE_ERROR, +                              g_file_error_from_errno (errno), +-                             "Failed to change permissions of directory %s: %m", ++                             "Failed to validate permissions of directory %s: %m", +                              path); +                 return FALSE; +         } +  ++        if ((stat_buffer.st_mode & ~S_IFMT) != mode) { ++                g_set_error (error, ++                             G_FILE_ERROR, ++                             g_file_error_from_errno (errno), ++                             "Directory %s has wrong mode %o; it should be %o", ++                             path, stat_buffer.st_mode, mode); ++                return FALSE; ++        } ++ +         return TRUE; + } +  + static gboolean + ensure_file_permissions (const char  *dir_path, +                          gint         file_mode, +                          GError     **error) + { +         GDir *dir = NULL; +         const gchar *filename; +         gint errsv = 0; +  +         dir = g_dir_open (dir_path, 0, error); +         if (dir == NULL) +                 return FALSE; +  +         while ((filename = g_dir_read_name (dir)) != NULL) { ++                GStatBuf stat_buffer = { 0 }; ++ +                 gchar *file_path = g_build_filename (dir_path, filename, NULL); +  +                 g_debug ("Changing permission of %s to %04o", file_path, file_mode); +-                if (g_chmod (file_path, file_mode) < 0) ++                g_chmod (file_path, file_mode); ++ ++                if (g_stat (file_path, &stat_buffer) < 0) +                         errsv = errno; +  ++                if ((stat_buffer.st_mode & ~S_IFMT) != file_mode) ++                        errsv = EACCES; ++ +                 g_free (file_path); +         } +  +         g_dir_close (dir); +  +         /* Report any errors after all chmod()s have been attempted. */ +         if (errsv != 0) { +                 g_set_error (error, +                              G_FILE_ERROR, +                              g_file_error_from_errno (errsv), +                              "Failed to change permissions of files in directory %s: %m", +                              dir_path); +                 return FALSE; +         } +  +         return TRUE; + } +  + static void + on_bus_acquired (GDBusConnection  *connection, +                  const gchar      *name, +                  gpointer          user_data) + { +         GMainLoop *loop = user_data; +         Daemon *daemon; +         g_autoptr(GError) error = NULL; +  +         if (!ensure_directory (ICONDIR, 0775, &error) || +             !ensure_directory (USERDIR, 0700, &error) || +             !ensure_file_permissions (USERDIR, 0600, &error)) { +                 g_printerr ("%s\n", error->message); +                 g_main_loop_quit (loop); +                 return; +         } +  +         daemon = daemon_new (); +         if (daemon == NULL) { +                 g_printerr ("Failed to initialize daemon\n"); +                 g_main_loop_quit (loop); +                 return; +         } +- +         openlog ("accounts-daemon", LOG_PID, LOG_DAEMON); +         syslog (LOG_INFO, "started daemon version %s", VERSION); +         closelog (); +         openlog ("accounts-daemon", 0, LOG_AUTHPRIV); + } +  + static void + on_name_lost (GDBusConnection  *connection, +               const gchar      *name, +               gpointer          user_data) + { +         GMainLoop *loop = user_data; +  +         g_debug ("got NameLost, exiting"); +         g_main_loop_quit (loop); + } +  + static gboolean debug; +  + static void + on_log_debug (const gchar *log_domain, +               GLogLevelFlags log_level, +               const gchar *message, +               gpointer user_data) + { +         g_autoptr(GString) string = NULL; +         const gchar *progname; +         int ret G_GNUC_UNUSED; +  +         string = g_string_new (NULL); +--  +2.31.1 + diff --git a/accountsservice.spec b/accountsservice.spec new file mode 100644 index 0000000..fe0f9ef --- /dev/null +++ b/accountsservice.spec @@ -0,0 +1,412 @@ +%global _hardened_build 1 + +Name:           accountsservice +Version:        0.6.55 +Release:        9%{?dist} +Summary:        D-Bus interfaces for querying and manipulating user account information +License:        GPLv3+ +URL:            https://www.freedesktop.org/wiki/Software/AccountsService/ + +#VCS: git:git://git.freedesktop.org/accountsservice +Source0:        http://www.freedesktop.org/software/accountsservice/accountsservice-%{version}.tar.xz +Source1:        user-template + +BuildRequires:  gettext-devel +BuildRequires:  pkgconfig(dbus-1) +BuildRequires:  glib2-devel +BuildRequires:  polkit-devel +BuildRequires:  systemd +BuildRequires:  systemd-devel +BuildRequires:  gobject-introspection-devel +BuildRequires:  gtk-doc +BuildRequires:  git +BuildRequires:  meson + +Requires:       polkit +Requires:       shadow-utils +%{?systemd_requires} + +Patch10001:     0001-data-don-t-send-change-updates-for-login-history.patch + +Patch20001:     0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch + +Patch30001:     0001-lib-save-os-when-creating-user.patch + +Patch40001:     0001-user-Introduce-user-templates-for-setting-default-se.patch + +Patch50001:     0001-daemon-Allow-SystemAccount-false-to-be-set-in-cache-.patch +Patch50002:     0002-main-Allow-cache-files-to-be-marked-immutable.patch + +%description +The accountsservice project provides a set of D-Bus interfaces for +querying and manipulating user account information and an implementation +of these interfaces, based on the useradd, usermod and userdel commands. + +%package libs +Summary: Client-side library to talk to accountsservice +Requires: %{name} = %{version}-%{release} + +%description libs +The accountsservice-libs package contains a library that can +be used by applications that want to interact with the accountsservice +daemon. + +%package devel +Summary: Development files for accountsservice-libs +Requires: %{name}-libs = %{version}-%{release} + +%description devel +The accountsservice-devel package contains headers and other +files needed to build applications that use accountsservice-libs. + + +%prep +%autosetup -S git + +%build +%meson -Dgtk_doc=true -Dsystemd=true -Duser_heuristics=true +%meson_build + +%install +%meson_install + +mkdir -p $RPM_BUILD_ROOT%{_datadir}/accountsservice/interfaces/ +mkdir -p $RPM_BUILD_ROOT%{_datadir}/accountsservice/user-templates $RPM_BUILD_ROOT%{_sysconfdir}/accountsservice/user-templates +cp $RPM_SOURCE_DIR/user-template $RPM_BUILD_ROOT%{_datadir}/accountsservice/user-templates/standard +cp $RPM_SOURCE_DIR/user-template $RPM_BUILD_ROOT%{_datadir}/accountsservice/user-templates/administrator + + +%find_lang accounts-service + +%ldconfig_scriptlets libs + +%post +%systemd_post accounts-daemon.service + +%preun +%systemd_preun accounts-daemon.service + +%postun +%systemd_postun accounts-daemon.service + +%files -f accounts-service.lang +%license COPYING +%doc README.md AUTHORS +%dir %{_sysconfdir}/accountsservice/user-templates +%dir %{_sysconfdir}/accountsservice +%{_sysconfdir}/dbus-1/system.d/org.freedesktop.Accounts.conf +%{_libexecdir}/accounts-daemon +%dir %{_datadir}/accountsservice/ +%dir %{_datadir}/accountsservice/interfaces/ +%{_datadir}/dbus-1/interfaces/org.freedesktop.Accounts.xml +%{_datadir}/dbus-1/interfaces/org.freedesktop.Accounts.User.xml +%{_datadir}/dbus-1/system-services/org.freedesktop.Accounts.service +%{_datadir}/polkit-1/actions/org.freedesktop.accounts.policy +%{_datadir}/accountsservice/interfaces/com.redhat.AccountsServiceUser.System.xml +%{_datadir}/accountsservice/user-templates/administrator +%{_datadir}/accountsservice/user-templates/standard +%{_datadir}/dbus-1/interfaces/com.redhat.AccountsServiceUser.System.xml +%dir %{_localstatedir}/lib/AccountsService/ +%dir %{_localstatedir}/lib/AccountsService/users +%dir %{_localstatedir}/lib/AccountsService/icons +%{_unitdir}/accounts-daemon.service + +%files libs +%{_libdir}/libaccountsservice.so.* +%{_libdir}/girepository-1.0/AccountsService-1.0.typelib + +%files devel +%{_includedir}/accountsservice-1.0 +%{_libdir}/libaccountsservice.so +%{_libdir}/pkgconfig/accountsservice.pc +%{_datadir}/gir-1.0/AccountsService-1.0.gir +%dir %{_datadir}/gtk-doc/html/libaccountsservice +%{_datadir}/gtk-doc/html/libaccountsservice/* + +%changelog +* Mon Oct 25 2021 Ray Strode <rstrode@redhat.com> - 0.6.55-9 +- Bring in RHEL-8 patches +  Resolves: #2014692 + +* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 0.6.55-8 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags +  Related: rhbz#1991688 + +* Thu Apr 15 2021 Mohan Boddu <mboddu@redhat.com> - 0.6.55-7 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Mon Jan 25 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.55-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Fri Sep 04 2020 Bastien Nocera <bnocera@redhat.com> - 0.6.55-5 ++ accountsservice-0.6.55-5 +- Own /usr/share/accountsservice + +* Fri Jul 31 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.55-4 +- Second attempt - Rebuilt for +  https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 27 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.55-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue Jan 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.55-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Thu Sep 26 2019 Benjamin Berg <bberg@redhat.com> - 0.6.55-1 +- Update to 0.6.55 +  Resolves: #1755838 + +* Wed Jul 24 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.54-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Thu Jan 31 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.54-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Mon Jan 21 2019 Alexandru-Sever Horin <alex.sever.h@gmail.com> - 0.6.54-4 +- Add patch from upstream to fix UID detection +  Resolves: #1646418 + +* Thu Jan 17 2019 Adam Williamson <awilliam@redhat.com> - 0.6.54-3 +- Explicitly enable systemd support (#1576903) (Elliott Sales de Andrade) + +* Mon Jan 14 2019 Björn Esser <besser82@fedoraproject.org> - 0.6.54-2 +- Rebuilt for libcrypt.so.2 (#1666033) + +* Sat Sep 29 2018 Ray Strode <rstrode@redhat.com> - 0.6.54-1 +- Update to 0.6.54 + +* Thu Sep 27 2018 Ray Strode <rstrode@redhat.com> - 0.6.53-1 +- Update to 0.6.53 + +* Mon Sep 24 2018 Adam Williamson <awilliam@redhat.com> - 0.6.50-1 +- Update to 0.6.50, plus a couple of backported patches +  Resolves: #1576903 + +* Thu Jul 12 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.49-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu May 10 2018 Ray Strode <rstrode@redhat.com> - 0.6.49-1 +- Update to 0.6.49 (brown bag release) + +* Thu May 10 2018 Ray Strode <rstrode@redhat.com> - 0.6.48-1 +- Update to 0.6.48 +  Resolves: #1575780 + +* Fri May 04 2018 Ray Strode <rstrode@redhat.com> - 0.6.47-2 +- fix crash on user deletion +  Resolves: #1573550 + +* Tue Apr 24 2018 Ray Strode <rstrode@redhat.com> - 0.6.47-1 +- Update to 0.6.47 + +* Sat Apr 21 2018 Peter Robinson <pbrobinson@fedoraproject.org> 0.4.46-1 +- Update to 0.6.46 +- Spec cleanup, use %%license + +* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.42-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sun Feb 04 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 0.6.42-8 +- Switch to %%ldconfig_scriptlets + +* Thu Jan 25 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 0.6.42-7 +- Fix systemd executions/requirements + +* Wed Jan 24 2018 Ray Strode <rstrode@redhat.com> - 0.6.42-6 +- Fix crash introduced by glibc/libxcrypt change +  https://fedoraproject.org/wiki/Changes/Replace_glibc_libcrypt_with_libxcrypt +  Resolves: #1538181 + +* Sat Jan 20 2018 Björn Esser <besser82@fedoraproject.org> - 0.6.42-5 +- Rebuilt for switch to libxcrypt + +* Wed Aug 02 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.42-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.42-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.42-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Jun 09 2016 Ray Strode <rstrode@redhat.com> - 0.6.42-1 +- Update to 0.6.42 +- Fixes systemd incompatibility + +* Tue May 31 2016 Ray Strode <rstrode@redhat.com> - 0.6.40-4 +- Don't create /root/.cache at startup +  Resolves: #1331926 + +* Wed Feb 03 2016 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.40-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Jun 16 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.40-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Jan 23 2015 Ray Strode <rstrode@redhat.com> 0.6.40-1 +- Update to 0.6.40 + +* Fri Oct 17 2014 Ray Strode <rstrode@redhat.com> 0.6.39-2 +- More ListCachedUsers race fixes (this time with SSSD) +  Related: #1147504 + +* Thu Oct 16 2014 Ray Strode <rstrode@redhat.com> 0.6.39-1 +- Update to 0.6.39 +- Fixes ListCachedUsers race at startup + +* Thu Sep 18 2014 Stef Walter <stefw@redhat.com> - 0.6.38-1 +- Update to 0.6.38 +- Fixes polkit policy rhbz#1094138 +- Remove dbus-glib-devel dependency, accountsservice uses gdbus now + +* Fri Aug 15 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.37-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Tue Jul 22 2014 Kalev Lember <kalevlember@gmail.com> - 0.6.37-2 +- Rebuilt for gobject-introspection 1.41.4 + +* Sat Jun 07 2014 Kalev Lember <kalevlember@gmail.com> - 0.6.37-1 +- Update to 0.6.37, drop upstreamed patches + +* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.35-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Fri Jan 10 2014 Matthias Clasen <mclasen@redhat.com> - 0.6.35-4 +- Consistently call userdel with -f + +* Wed Nov 20 2013 Ray Strode <rstrode@redhat.com> 0.6.35-3 +- Only treat users < 1000 as system users +- only use user heuristics on the range 500-1000 + +* Mon Nov 11 2013 Ray Strode <rstrode@redhat.com> 0.6.35-2 +- pass --enable-user-heuristics which fedora needs so users +  with UIDs less than 1000 show up in the user list. + +* Mon Oct 28 2013 Ray Strode <rstrode@redhat.com> 0.6.35-1 +- Update to 0.6.35 +  Related: #1013721 + +* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.34-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jun 11 2013 Ray Strode <rstrode@redhat.com> 0.6.34-1 +- Update to 0.6.34 + +* Tue Jun 11 2013 Matthias Clasen <mclasen@redhat.com> - 0.6.33-1 +- Update to 0.6.33 + +* Tue May 14 2013 Matthias Clasen <mclasen@redhat.com> - 0.6.32-1 +- Update to 0.6.32 + +* Thu Apr 18 2013 Matthias Clasen <mclasen@redhat.com> - 0.6.31-2 +- Hardened build + +* Tue Apr 16 2013 Matthias Clasen <mclasen@redhat.com> - 0.6.31-1 +- Update to 0.6.31 + +* Wed Feb 13 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.30-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Jan 16 2013 Richard Hughes <rhughes@redhat.com> - 0.6.30-1 +- Update to 0.6.30 + +* Fri Nov 16 2012 Matthias Clasen <mclasen@redhat.com> - 0.6.26-1 +- Update to 0.6.26 + +* Tue Oct  2 2012 Matthias Clasen <mclasen@redhat.com> - 0.6.25-2 +- Update to 0.6.25 +- Use systemd scriptlets (#856649) + +* Wed Jul 18 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.22-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jul 14 2012 Ville Skyttä <ville.skytta@iki.fi> - 0.6.22-2 +- Add ldconfig scriptlets to -libs. + +* Thu Jun 28 2012 Ray Strode <rstrode@redhat.com> 0.6.22-1 +- Update to 0.6.22. +- Fixes CVE-2012-2737 - local file disclosure +  Related:  #832532 + +* Thu May 30 2012 Matthias Clasen <mclasen@redhatcom> 0.6.21-1 +- Update to 0.6.21 + +* Fri May 04 2012 Ray Strode <rstrode@redhat.com> 0.6.20-1 +- Update to 0.6.20. Should fix user list. +  Related: #814690 + +* Thu May 03 2012 Ray Strode <rstrode@redhat.com> 0.6.19-1 +- Update to 0.6.19 +  Allows user deletion of logged in users +  Related: #814690 + +* Wed Apr 11 2012 Matthias Clasen <mclsaen@redhat.com> - 0.6.18-1 +- Update to 0.6.18 + +* Tue Mar 27 2012 Ray Strode <rstrode@redhat.com> 0.6.17-1 +- Update to latest release + +* Sun Mar  4 2012 Peter Robinson <pbrobinson@fedoraproject.org> - 0.6.15-4 +- Fix unitdir with usrmove + +* Thu Jan 12 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.15-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Tue Nov 29 2011 Matthias Clasen <mclasen@redhat.com> 0.6.15-2 +- Make resetting user icons work +- Update to 0.6.15 +- Fixes session chooser at login screen when logged into vt + +* Wed Sep 21 2011 Ray Strode <rstrode@redhat.com> 0.6.14-2 +- Fix wtmp loading so users coming from the network are +  remembered in the user list in subsequent boots + +* Wed Sep 21 2011 Ray Strode <rstrode@redhat.com> 0.6.14-1 +- Update to 0.6.14 + +* Sun Sep  4 2011 Matthias Clasen <mclasen@redhat.com> - 0.6.13-3 +- Fix fast user switching + +* Mon Aug 15 2011 Kalev Lember <kalevlember@gmail.com> - 0.6.13-2 +- Rebuilt for rpm bug #728707 + +* Tue Jul 19 2011 Matthias Clasen <mclasen@redhat.com> - 0.6.13-1 +- Update to 0.6.13 +- Drop ConsoleKit dependency + +* Mon Jun 06 2011 Ray Strode <rstrode@redhat.com> 0.6.12-1 +- Update to latest release + +* Wed May 18 2011 Matthias Clasen <mclasen@redhat.com> 0.6.11-1 +- Update to 0.6.11 + +* Mon Feb 07 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Feb 02 2011 Ray Strode <rstrode@redhat.com> 0.6.3-1 +- Update to 0.6.3 + +* Thu Jan 27 2011 Matthias Clasen <mclasen@redhat.com> 0.6.2-1 +- Update to 0.6.2 + +* Wed Jul 21 2010 Matthias Clasen <mclasen@redhat.com> 0.6.1-1 +- Update to 0.6.1 +- Install systemd unit file + +* Mon Apr  5 2010 Matthias Clasen <mclasen@redhat.com> 0.6-2 +- Always emit changed signal on icon change + +* Tue Mar 30 2010 Matthias Clasen <mclasen@redhat.com> 0.6-1 +- Update to 0.6 + +* Mon Mar 22 2010 Matthias Clasen <mclasen@redhat.com> 0.5-1 +- Update to 0.5 + +* Mon Feb 22 2010 Bastien Nocera <bnocera@redhat.com> 0.4-3 +- Fix directory ownership + +* Mon Feb 22 2010 Bastien Nocera <bnocera@redhat.com> 0.4-2 +- Add missing directories to the filelist + +* Fri Jan 29 2010 Matthias Clasen <mclasen@redhat.com> 0.4-1 +- @@ -0,0 +1 @@ +6e4c6fbd490260cfe17de2e76f5d803a  accountsservice-0.6.55.tar.xz diff --git a/user-template b/user-template new file mode 100644 index 0000000..d36c8e9 --- /dev/null +++ b/user-template @@ -0,0 +1,13 @@ +# This file contains defaults for new users. To edit, first +# copy it to /etc/accountsservice/user-templates and make changes +# there +[Template] +EnvironmentFiles=/etc/os-release; + +[com.redhat.AccountsServiceUser.System] +id='${ID}' +version-id='${VERSION_ID}' + +[User] +Session=gnome +Icon=${HOME}/.face  | 
