Description: Filter out users which have an UID lower than UID_MIN specified in
 /etc/login.defs
Author: Alessio Treglia <alessio@debian.org>
Forwarded: no
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=673095
---
 src/daemon.c |  129 ++++++++++++++++++++++++++++++-----------------------------
 src/daemon.h |    3 -
 src/user.c   |    3 -
 3 files changed, 68 insertions(+), 67 deletions(-)

--- accountsservice.orig/src/daemon.c
+++ accountsservice/src/daemon.c
@@ -50,10 +50,15 @@
 #define PATH_NOLOGIN "/sbin/nologin"
 #define PATH_FALSE "/bin/false"
 #define PATH_GDM_CUSTOM "/etc/gdm3/daemon.conf"
+#define PATH_LOGIN_DEFS "/etc/login.defs"
 #ifdef HAVE_UTMPX_H
 #define PATH_WTMP _PATH_WTMPX
 #endif
 
+#ifndef FALLBACK_MINIMAL_UID
+#define FALLBACK_MINIMAL_UID 500
+#endif
+
 static const char *default_excludes[] = {
         "bin",
         "root",
@@ -96,6 +101,7 @@ struct DaemonPrivate {
 
         GHashTable *users;
         GHashTable *exclusions;
+        uid_t minimal_uid;
 
         User *autologin;
 
@@ -168,73 +174,18 @@ error_get_type (void)
 gboolean
 daemon_local_user_is_excluded (Daemon      *daemon,
                                const gchar *username,
-                               const gchar *shell,
-                               const gchar *password_hash)
+                               uid_t uid)
 {
-        int ret;
 
         if (g_hash_table_lookup (daemon->priv->exclusions, username)) {
                 return TRUE;
         }
 
-        ret = FALSE;
-
-        if (shell != NULL) {
-                char *basename, *nologin_basename, *false_basename;
-
-#ifdef HAVE_GETUSERSHELL
-                char *valid_shell;
-
-                ret = TRUE;
-                setusershell ();
-                while ((valid_shell = getusershell ()) != NULL) {
-                        if (g_strcmp0 (shell, valid_shell) != 0)
-                                continue;
-                        ret = FALSE;
-                }
-                endusershell ();
-#endif
-
-                basename = g_path_get_basename (shell);
-                nologin_basename = g_path_get_basename (PATH_NOLOGIN);
-                false_basename = g_path_get_basename (PATH_FALSE);
-
-                if (shell[0] == '\0') {
-                        ret = TRUE;
-                } else if (g_strcmp0 (basename, nologin_basename) == 0) {
-                        ret = TRUE;
-                } else if (g_strcmp0 (basename, false_basename) == 0) {
-                        ret = TRUE;
-                }
-
-                g_free (basename);
-                g_free (nologin_basename);
-                g_free (false_basename);
-        }
-
-        if (password_hash != NULL) {
-                /* skip over the account-is-locked '!' prefix if present */
-                if (password_hash[0] == '!')
-                    password_hash++;
-
-                if (password_hash[0] != '\0') {
-                        /* modern hashes start with "$n$" */
-                        if (password_hash[0] == '$') {
-                                if (strlen (password_hash) < 4)
-                                    ret = TRUE;
-
-                        /* DES crypt is base64 encoded [./A-Za-z0-9]*
-                         */
-                        } else if (!g_ascii_isalnum (password_hash[0]) &&
-                                   password_hash[0] != '.' &&
-                                   password_hash[0] != '/') {
-                                ret = TRUE;
-                        }
-                }
-
+        if (uid < daemon->priv->minimal_uid) {
+                return TRUE;
         }
 
-        return ret;
+        return FALSE;
 }
 
 #ifdef HAVE_UTMPX_H
@@ -516,7 +467,7 @@ load_entries (Daemon             *daemon
                         break;
 
                 /* Skip system users... */
-                if (daemon_local_user_is_excluded (daemon, pwent->pw_name, pwent->pw_shell, NULL)) {
+                if (daemon_local_user_is_excluded (daemon, pwent->pw_name, pwent->pw_uid)) {
                         g_debug ("skipping user: %s", pwent->pw_name);
                         continue;
                 }
@@ -743,6 +694,59 @@ on_gdm_monitor_changed (GFileMonitor
         queue_reload_autologin (daemon);
 }
 
+static uid_t
+get_minimal_uid (void)
+{
+        GError *error;
+        char *contents;
+        gboolean contents_loaded;
+        const char *uid_min_string, *start_of_uid_string;
+        char *end;
+        uid_t uid = FALLBACK_MINIMAL_UID;
+        gint64 uid_as_number;
+
+        error = NULL;
+        contents = NULL;
+        contents_loaded = g_file_get_contents (PATH_LOGIN_DEFS, &contents, NULL, &error);
+        if (!contents_loaded) {
+                g_debug ("unable to read " PATH_LOGIN_DEFS ": %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        uid_min_string = strstr (contents, "UID_MIN");
+
+        if (uid_min_string == NULL ||
+            (uid_min_string != contents && uid_min_string[-1] != '\n')) {
+                g_debug (PATH_LOGIN_DEFS " does not have a UID_MIN field");
+                goto out;
+        }
+
+        start_of_uid_string = uid_min_string + strlen ("UID_MIN");
+
+        if (start_of_uid_string == '\0') {
+                g_debug (PATH_LOGIN_DEFS " contains UID_MIN key with no value");
+                goto out;
+        }
+
+
+        uid_as_number = g_ascii_strtoll (start_of_uid_string, &end, 10);
+        if (!g_ascii_isspace (*end) && *end != '\0') {
+                g_debug (PATH_LOGIN_DEFS " contains non-numerical value for UID_MIN");
+                goto out;
+        }
+
+        if (uid_as_number < 0 || ((uid_t) uid_as_number) != uid_as_number) {
+                g_debug (PATH_LOGIN_DEFS " contains out-of-range value for UID_MIN");
+                goto out;
+        }
+
+        uid = (uid_t) uid_as_number;
+out:
+        g_free (contents);
+        return uid;
+}
+
 static void
 daemon_init (Daemon *daemon)
 {
@@ -752,6 +756,7 @@ daemon_init (Daemon *daemon)
 
         daemon->priv = DAEMON_GET_PRIVATE (daemon);
 
+        daemon->priv->minimal_uid = get_minimal_uid ();
         daemon->priv->exclusions = g_hash_table_new_full (g_str_hash,
                                                           g_str_equal,
                                                           g_free,
@@ -1075,16 +1080,14 @@ finish_list_cached_users (gpointer user_
         const gchar *name;
         User *user;
         uid_t uid;
-        const gchar *shell;
 
         object_paths = g_ptr_array_new ();
 
         g_hash_table_iter_init (&iter, data->daemon->priv->users);
         while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&user)) {
                 uid = user_get_uid (user);
-                shell = user_get_shell (user);
 
-                if (daemon_local_user_is_excluded (data->daemon, name, shell, NULL)) {
+                if (daemon_local_user_is_excluded (data->daemon, name, uid)) {
                         g_debug ("user %s %ld excluded", name, (long) uid);
                         continue;
                 }
--- accountsservice.orig/src/daemon.h
+++ accountsservice/src/daemon.h
@@ -78,8 +78,7 @@ User *daemon_local_find_user_by_name (Da
 User *daemon_local_get_automatic_login_user (Daemon         *daemon);
 gboolean daemon_local_user_is_excluded (Daemon              *daemon,
                                         const gchar         *name,
-                                        const gchar         *shell,
-                                        const gchar         *password_hash);
+                                        uid_t                uid);
 
 typedef void (*AuthorizedCallback)   (Daemon                *daemon,
                                       User                  *user,
--- accountsservice.orig/src/user.c
+++ accountsservice/src/user.c
@@ -290,8 +290,7 @@ user_update_from_pwent (User          *u
          */
         user->system_account = daemon_local_user_is_excluded (user->daemon,
                                                               user->user_name,
-                                                              pwent->pw_shell,
-                                                              passwd);
+                                                              pwent->pw_uid);
 
         g_object_thaw_notify (G_OBJECT (user));
 
