diff --git a/.accountsservice.metadata b/.accountsservice.metadata
new file mode 100644
index 0000000..6799259
--- /dev/null
+++ b/.accountsservice.metadata
@@ -0,0 +1 @@
+bab4f37144196d8ba06195bc72b4a9937c62b9fd SOURCES/accountsservice-0.6.55.tar.xz
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..37d0a71
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/accountsservice-0.6.55.tar.xz
diff --git a/SOURCES/0001-daemon-Allow-SystemAccount-false-to-be-set-in-cache-.patch b/SOURCES/0001-daemon-Allow-SystemAccount-false-to-be-set-in-cache-.patch
new file mode 100644
index 0000000..84d5265
--- /dev/null
+++ b/SOURCES/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/SOURCES/0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch b/SOURCES/0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch
new file mode 100644
index 0000000..5185345
--- /dev/null
+++ b/SOURCES/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/SOURCES/0001-data-don-t-send-change-updates-for-login-history.patch b/SOURCES/0001-data-don-t-send-change-updates-for-login-history.patch
new file mode 100644
index 0000000..94d6c7a
--- /dev/null
+++ b/SOURCES/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/SOURCES/0001-lib-save-os-when-creating-user.patch b/SOURCES/0001-lib-save-os-when-creating-user.patch
new file mode 100644
index 0000000..08d953d
--- /dev/null
+++ b/SOURCES/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/SOURCES/0001-user-Introduce-user-templates-for-setting-default-se.patch b/SOURCES/0001-user-Introduce-user-templates-for-setting-default-se.patch
new file mode 100644
index 0000000..001de67
--- /dev/null
+++ b/SOURCES/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/SOURCES/0002-main-Allow-cache-files-to-be-marked-immutable.patch b/SOURCES/0002-main-Allow-cache-files-to-be-marked-immutable.patch
new file mode 100644
index 0000000..991a879
--- /dev/null
+++ b/SOURCES/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/SOURCES/user-template b/SOURCES/user-template
new file mode 100644
index 0000000..d36c8e9
--- /dev/null
+++ b/SOURCES/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
diff --git a/SPECS/accountsservice.spec b/SPECS/accountsservice.spec
new file mode 100644
index 0000000..175f26e
--- /dev/null
+++ b/SPECS/accountsservice.spec
@@ -0,0 +1,417 @@
+%global _hardened_build 1
+
+Name:           accountsservice
+Version:        0.6.55
+Release:        10%{?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/
+%attr(0700, root, root) %dir %{_localstatedir}/lib/AccountsService/users
+%attr(0775, root, root) %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
+* Thu Feb 24 2022 Ray Strode <rstrode@redhat.com> - 0.6.55-10
+- Synchronize permissions and group ownership for icon and users dirs
+  between rpm file manifest and daemon expectations.
+  Resolves: #2057576
+
+* 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
+-