Ray Strode 8b27c7
From ba13b59cb91ec67c86b3e3fb390d91db01df8963 Mon Sep 17 00:00:00 2001
Ray Strode 8b27c7
From: Ray Strode <rstrode@redhat.com>
Ray Strode 8b27c7
Date: Fri, 15 Nov 2013 10:11:15 -0500
Ray Strode 8b27c7
Subject: [PATCH] Change up user classification logic again
Ray Strode 8b27c7
Ray Strode 8b27c7
relying on login.defs is fragile, and the
Ray Strode 8b27c7
user heuristics are fragile.
Ray Strode 8b27c7
Ray Strode 8b27c7
This commit requires an explicit uid minimum
Ray Strode 8b27c7
get configured, and heuristics now only get
Ray Strode 8b27c7
applied to the specific problematic range
Ray Strode 8b27c7
they were added to address.
Ray Strode 8b27c7
Ray Strode 8b27c7
https://bugs.freedesktop.org/show_bug.cgi?id=71801
Ray Strode 8b27c7
---
Ray Strode 8b27c7
 configure.ac        |   8 +++-
Ray Strode 8b27c7
 src/user-classify.c | 129 ++++++++++------------------------------------------
Ray Strode 8b27c7
 2 files changed, 32 insertions(+), 105 deletions(-)
Ray Strode 8b27c7
Ray Strode 8b27c7
diff --git a/configure.ac b/configure.ac
Ray Strode 8b27c7
index cb1fcda..39c5b92 100644
Ray Strode 8b27c7
--- a/configure.ac
Ray Strode 8b27c7
+++ b/configure.ac
Ray Strode 8b27c7
@@ -28,65 +28,71 @@ AC_SUBST(LT_AGE)
Ray Strode 8b27c7
 PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.37.3 gio-unix-2.0)
Ray Strode 8b27c7
 PKG_CHECK_MODULES(POLKIT, gio-unix-2.0 polkit-gobject-1)
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 AM_MAINTAINER_MODE([enable])
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 # client library dependencies
Ray Strode 8b27c7
 LIBACCOUNTSSERVICE_LIBS="$GIO_LIBS"
Ray Strode 8b27c7
 AC_SUBST(LIBACCOUNTSSERVICE_LIBS)
Ray Strode 8b27c7
 LIBACCOUNTSSERVICE_CFLAGS="$GIO_CFLAGS"
Ray Strode 8b27c7
 AC_SUBST(LIBACCOUNTSSERVICE_CFLAGS)
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 GOBJECT_INTROSPECTION_CHECK([0.9.12])
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 dnl ---------------------------------------------------------------------------
Ray Strode 8b27c7
 dnl - Core configuration
Ray Strode 8b27c7
 dnl ---------------------------------------------------------------------------
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 AC_ARG_ENABLE(admin-group,
Ray Strode 8b27c7
         [AS_HELP_STRING([--enable-admin-group],[Set group for administrative accounts @<:@default=auto@:>@])],
Ray Strode 8b27c7
         ,enable_admin_group=auto)
Ray Strode 8b27c7
 AS_IF([test x$enable_admin_group = xauto], [
Ray Strode 8b27c7
   AC_CHECK_FILE(/etc/redhat-release, enable_admin_group=wheel)
Ray Strode 8b27c7
   AC_CHECK_FILE(/etc/debian_version, enable_admin_group=sudo)
Ray Strode 8b27c7
   AS_IF([test x$enable_admin_group = xauto], [
Ray Strode 8b27c7
     enable_admin_group=wheel
Ray Strode 8b27c7
   ])
Ray Strode 8b27c7
 ])
Ray Strode 8b27c7
 AC_DEFINE_UNQUOTED([ADMIN_GROUP], ["$enable_admin_group"], [Define to the group for administrator users])
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 AC_ARG_ENABLE(user-heuristics,
Ray Strode 8b27c7
-        [AS_HELP_STRING([--enable-user-heuristics],[Enable heuristics for guessing system vs. human users])],
Ray Strode 8b27c7
+        [AS_HELP_STRING([--enable-user-heuristics],[Enable heuristics for guessing system vs. human users in the range 500-minimum-uid])],
Ray Strode 8b27c7
         [if test "$enableval" = yes; then
Ray Strode 8b27c7
            AC_DEFINE([ENABLE_USER_HEURISTICS], , [System vs. human user heuristics enabled])
Ray Strode 8b27c7
         fi])
Ray Strode 8b27c7
 
Ray Strode 8b27c7
+AC_ARG_WITH(minimum-uid,
Ray Strode 8b27c7
+        [AS_HELP_STRING([--with-minimum-uid],[Set minimum uid for human users])],
Ray Strode 8b27c7
+        ,with_minimum_uid=1000)
Ray Strode 8b27c7
+
Ray Strode 8b27c7
+AC_DEFINE_UNQUOTED([MINIMUM_UID], $with_minimum_uid, [Define to the minumum UID of human users])
Ray Strode 8b27c7
+
Ray Strode 8b27c7
 dnl ---------------------------------------------------------------------------
Ray Strode 8b27c7
 dnl - coverage
Ray Strode 8b27c7
 dnl ---------------------------------------------------------------------------
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 AC_MSG_CHECKING([whether to build with gcov testing])
Ray Strode 8b27c7
 AC_ARG_ENABLE([coverage],
Ray Strode 8b27c7
               AS_HELP_STRING([--enable-coverage],
Ray Strode 8b27c7
                              [Whether to enable gcov code coverage]),
Ray Strode 8b27c7
               [], [enable_coverage=no])
Ray Strode 8b27c7
 AC_MSG_RESULT([$enable_coverage])
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 if test "$enable_coverage" = "yes"; then
Ray Strode 8b27c7
 	if test "$GCC" != "yes"; then
Ray Strode 8b27c7
 		AC_MSG_ERROR(Coverage testing requires GCC)
Ray Strode 8b27c7
 	fi
Ray Strode 8b27c7
 	CFLAGS="$CFLAGS -O0 -g --coverage"
Ray Strode 8b27c7
 fi
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 AM_CONDITIONAL([WITH_COVERAGE], [test "$enable_coverage" = "yes"])
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 dnl ---------------------------------------------------------------------------
Ray Strode 8b27c7
 dnl - Warnings
Ray Strode 8b27c7
 dnl ---------------------------------------------------------------------------
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 AC_ARG_ENABLE(more-warnings,
Ray Strode 8b27c7
               AS_HELP_STRING([--enable-more-warnings],
Ray Strode 8b27c7
                              [Maximum compiler warnings]),
Ray Strode 8b27c7
               set_more_warnings="$enableval",[
Ray Strode 8b27c7
               if test -d $srcdir/.git; then
Ray Strode 8b27c7
                 set_more_warnings=yes
Ray Strode 8b27c7
diff --git a/src/user-classify.c b/src/user-classify.c
Ray Strode 8b27c7
index b68c9ae..69e6809 100644
Ray Strode 8b27c7
--- a/src/user-classify.c
Ray Strode 8b27c7
+++ b/src/user-classify.c
Ray Strode 8b27c7
@@ -1,248 +1,169 @@
Ray Strode 8b27c7
 /*
Ray Strode 8b27c7
  * Copyright (C) 2009-2010 Red Hat, Inc.
Ray Strode 8b27c7
  * Copyright (C) 2013 Canonical Limited
Ray Strode 8b27c7
  *
Ray Strode 8b27c7
  * This program is free software; you can redistribute it and/or modify
Ray Strode 8b27c7
  * it under the terms of the GNU General Public License as published by
Ray Strode 8b27c7
  * the Free Software Foundation; either version 3 of the licence, or
Ray Strode 8b27c7
  * (at your option) any later version.
Ray Strode 8b27c7
  *
Ray Strode 8b27c7
  * This program is distributed in the hope that it will be useful,
Ray Strode 8b27c7
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
Ray Strode 8b27c7
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Ray Strode 8b27c7
  * GNU General Public License for more details.
Ray Strode 8b27c7
  *
Ray Strode 8b27c7
  * You should have received a copy of the GNU General Public License
Ray Strode 8b27c7
  * along with this program; if not, write to the Free Software
Ray Strode 8b27c7
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Ray Strode 8b27c7
  *
Ray Strode 8b27c7
  * Authors: Ryan Lortie <desrt@desrt.ca>
Ray Strode 8b27c7
  *          Matthias Clasen <mclasen@redhat.com>
Ray Strode 8b27c7
  */
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 #include "config.h"
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 #include "user-classify.h"
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 #include <string.h>
Ray Strode 8b27c7
 
Ray Strode 8b27c7
-#ifdef ENABLE_USER_HEURISTICS
Ray Strode 8b27c7
 static const char *default_excludes[] = {
Ray Strode 8b27c7
         "bin",
Ray Strode 8b27c7
         "root",
Ray Strode 8b27c7
         "daemon",
Ray Strode 8b27c7
         "adm",
Ray Strode 8b27c7
         "lp",
Ray Strode 8b27c7
         "sync",
Ray Strode 8b27c7
         "shutdown",
Ray Strode 8b27c7
         "halt",
Ray Strode 8b27c7
         "mail",
Ray Strode 8b27c7
         "news",
Ray Strode 8b27c7
         "uucp",
Ray Strode 8b27c7
         "operator",
Ray Strode 8b27c7
         "nobody",
Ray Strode 8b27c7
         "nobody4",
Ray Strode 8b27c7
         "noaccess",
Ray Strode 8b27c7
         "postgres",
Ray Strode 8b27c7
         "pvm",
Ray Strode 8b27c7
         "rpm",
Ray Strode 8b27c7
         "nfsnobody",
Ray Strode 8b27c7
         "pcap",
Ray Strode 8b27c7
         "mysql",
Ray Strode 8b27c7
         "ftp",
Ray Strode 8b27c7
         "games",
Ray Strode 8b27c7
         "man",
Ray Strode 8b27c7
         "at",
Ray Strode 8b27c7
         "gdm",
Ray Strode 8b27c7
         "gnome-initial-setup"
Ray Strode 8b27c7
 };
Ray Strode 8b27c7
 
Ray Strode 8b27c7
-#define PATH_NOLOGIN "/sbin/nologin"
Ray Strode 8b27c7
-#define PATH_FALSE "/bin/false"
Ray Strode 8b27c7
-
Ray Strode 8b27c7
 static gboolean
Ray Strode 8b27c7
-user_classify_is_excluded_by_heuristics (const gchar *username,
Ray Strode 8b27c7
-                                         const gchar *shell,
Ray Strode 8b27c7
-                                         const gchar *password_hash)
Ray Strode 8b27c7
+user_classify_is_blacklisted (const char *username)
Ray Strode 8b27c7
 {
Ray Strode 8b27c7
         static GHashTable *exclusions;
Ray Strode 8b27c7
-        gboolean ret = FALSE;
Ray Strode 8b27c7
 
Ray Strode 8b27c7
         if (exclusions == NULL) {
Ray Strode 8b27c7
                 guint i;
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                 exclusions = g_hash_table_new (g_str_hash, g_str_equal);
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                 for (i = 0; i < G_N_ELEMENTS (default_excludes); i++) {
Ray Strode 8b27c7
                         g_hash_table_add (exclusions, (gpointer) default_excludes[i]);
Ray Strode 8b27c7
                 }
Ray Strode 8b27c7
         }
Ray Strode 8b27c7
 
Ray Strode 8b27c7
         if (g_hash_table_contains (exclusions, username)) {
Ray Strode 8b27c7
                 return TRUE;
Ray Strode 8b27c7
         }
Ray Strode 8b27c7
 
Ray Strode 8b27c7
+        return FALSE;
Ray Strode 8b27c7
+}
Ray Strode 8b27c7
+
Ray Strode 8b27c7
+#define PATH_NOLOGIN "/sbin/nologin"
Ray Strode 8b27c7
+#define PATH_FALSE "/bin/false"
Ray Strode 8b27c7
+
Ray Strode 8b27c7
+#ifdef ENABLE_USER_HEURISTICS
Ray Strode 8b27c7
+static gboolean
Ray Strode 8b27c7
+user_classify_is_excluded_by_heuristics (const gchar *username,
Ray Strode 8b27c7
+                                         const gchar *shell,
Ray Strode 8b27c7
+                                         const gchar *password_hash)
Ray Strode 8b27c7
+{
Ray Strode 8b27c7
+        gboolean ret = FALSE;
Ray Strode 8b27c7
+
Ray Strode 8b27c7
         if (shell != NULL) {
Ray Strode 8b27c7
                 char *basename, *nologin_basename, *false_basename;
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 #ifdef HAVE_GETUSERSHELL
Ray Strode 8b27c7
                 char *valid_shell;
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                 ret = TRUE;
Ray Strode 8b27c7
                 setusershell ();
Ray Strode 8b27c7
                 while ((valid_shell = getusershell ()) != NULL) {
Ray Strode 8b27c7
                         if (g_strcmp0 (shell, valid_shell) != 0)
Ray Strode 8b27c7
                                 continue;
Ray Strode 8b27c7
                         ret = FALSE;
Ray Strode 8b27c7
                 }
Ray Strode 8b27c7
                 endusershell ();
Ray Strode 8b27c7
 #endif
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                 basename = g_path_get_basename (shell);
Ray Strode 8b27c7
                 nologin_basename = g_path_get_basename (PATH_NOLOGIN);
Ray Strode 8b27c7
                 false_basename = g_path_get_basename (PATH_FALSE);
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                 if (shell[0] == '\0') {
Ray Strode 8b27c7
                         ret = TRUE;
Ray Strode 8b27c7
                 } else if (g_strcmp0 (basename, nologin_basename) == 0) {
Ray Strode 8b27c7
                         ret = TRUE;
Ray Strode 8b27c7
                 } else if (g_strcmp0 (basename, false_basename) == 0) {
Ray Strode 8b27c7
                         ret = TRUE;
Ray Strode 8b27c7
                 }
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                 g_free (basename);
Ray Strode 8b27c7
                 g_free (nologin_basename);
Ray Strode 8b27c7
                 g_free (false_basename);
Ray Strode 8b27c7
         }
Ray Strode 8b27c7
 
Ray Strode 8b27c7
         if (password_hash != NULL) {
Ray Strode 8b27c7
                 /* skip over the account-is-locked '!' prefix if present */
Ray Strode 8b27c7
                 if (password_hash[0] == '!')
Ray Strode 8b27c7
                     password_hash++;
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                 if (password_hash[0] != '\0') {
Ray Strode 8b27c7
                         /* modern hashes start with "$n$" */
Ray Strode 8b27c7
                         if (password_hash[0] == '$') {
Ray Strode 8b27c7
                                 if (strlen (password_hash) < 4)
Ray Strode 8b27c7
                                     ret = TRUE;
Ray Strode 8b27c7
 
Ray Strode 8b27c7
                         /* DES crypt is base64 encoded [./A-Za-z0-9]*
Ray Strode 8b27c7
                          */
Ray Strode 8b27c7
                         } else if (!g_ascii_isalnum (password_hash[0]) &&
Ray Strode 8b27c7
                                    password_hash[0] != '.' &&
Ray Strode 8b27c7
                                    password_hash[0] != '/') {
Ray Strode 8b27c7
                                 ret = TRUE;
Ray Strode 8b27c7
                         }
Ray Strode 8b27c7
                 }
Ray Strode 8b27c7
 
Ray Strode 8b27c7
         }
Ray Strode 8b27c7
 
Ray Strode 8b27c7
         return ret;
Ray Strode 8b27c7
 }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-#else /* ENABLE_USER_HEURISTICS */
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-static gboolean
Ray Strode 8b27c7
-user_classify_parse_login_defs_field (const gchar *contents,
Ray Strode 8b27c7
-                                      const gchar *key,
Ray Strode 8b27c7
-                                      uid_t       *result)
Ray Strode 8b27c7
-{
Ray Strode 8b27c7
-        gsize key_len;
Ray Strode 8b27c7
-        gint64 value;
Ray Strode 8b27c7
-        gchar *end;
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        key_len = strlen (key);
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        for (;;) {
Ray Strode 8b27c7
-                /* Our key has to be at the start of the line, followed by whitespace */
Ray Strode 8b27c7
-                if (strncmp (contents, key, key_len) == 0 && g_ascii_isspace (contents[key_len])) {
Ray Strode 8b27c7
-                        /* Found it.  Move contents past the key itself and break out. */
Ray Strode 8b27c7
-                        contents += key_len;
Ray Strode 8b27c7
-                        break;
Ray Strode 8b27c7
-                }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-                /* Didn't find it.  Find the end of the line. */
Ray Strode 8b27c7
-                contents = strchr (contents, '\n');
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-                /* EOF? */
Ray Strode 8b27c7
-                if (!contents) {
Ray Strode 8b27c7
-                        /* We didn't find the field... */
Ray Strode 8b27c7
-                        return FALSE;
Ray Strode 8b27c7
-                }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-                /* Start at the beginning of the next line on next iteration. */
Ray Strode 8b27c7
-                contents++;
Ray Strode 8b27c7
-        }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        /* 'contents' now points at the whitespace character just after
Ray Strode 8b27c7
-         * the field name.  strtoll can deal with that.
Ray Strode 8b27c7
-         */
Ray Strode 8b27c7
-        value = g_ascii_strtoll (contents, &end, 10);
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        if (*end && !g_ascii_isspace (*end)) {
Ray Strode 8b27c7
-                g_warning ("Trailing junk after '%s' field in login.defs", key);
Ray Strode 8b27c7
-                return FALSE;
Ray Strode 8b27c7
-        }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        if (value <= 0 || value >= G_MAXINT32) {
Ray Strode 8b27c7
-                g_warning ("Value for '%s' field out of range", key);
Ray Strode 8b27c7
-                return FALSE;
Ray Strode 8b27c7
-        }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        *result = value;
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        return TRUE;
Ray Strode 8b27c7
-}
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-static void
Ray Strode 8b27c7
-user_classify_read_login_defs (uid_t *min_uid,
Ray Strode 8b27c7
-                               uid_t *max_uid)
Ray Strode 8b27c7
-{
Ray Strode 8b27c7
-        GError *error = NULL;
Ray Strode 8b27c7
-        char *contents;
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        if (!g_file_get_contents ("/etc/login.defs", &contents, NULL, &error)) {
Ray Strode 8b27c7
-                g_warning ("Could not open /etc/login.defs: %s.  Falling back to default human uid range of %d to %d",
Ray Strode 8b27c7
-                           error->message, (int) *min_uid, (int) *max_uid);
Ray Strode 8b27c7
-                g_error_free (error);
Ray Strode 8b27c7
-                return;
Ray Strode 8b27c7
-        }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        if (!user_classify_parse_login_defs_field (contents, "UID_MIN", min_uid)) {
Ray Strode 8b27c7
-                g_warning ("Could not find UID_MIN value in login.defs.  Using default of %d", (int) *min_uid);
Ray Strode 8b27c7
-        }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        if (!user_classify_parse_login_defs_field (contents, "UID_MAX", max_uid)) {
Ray Strode 8b27c7
-                g_warning ("Could not find UID_MIN value in login.defs.  Using default of %d", (int) *max_uid);
Ray Strode 8b27c7
-        }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        g_free (contents);
Ray Strode 8b27c7
-}
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-static gboolean
Ray Strode 8b27c7
-user_classify_is_in_human_range (uid_t uid)
Ray Strode 8b27c7
-{
Ray Strode 8b27c7
-        static uid_t min_uid = 1000, max_uid = 60000;
Ray Strode 8b27c7
-        static gboolean initialised;
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        if (!initialised) {
Ray Strode 8b27c7
-                user_classify_read_login_defs (&min_uid, &max_uid);
Ray Strode 8b27c7
-                initialised = TRUE;
Ray Strode 8b27c7
-        }
Ray Strode 8b27c7
-
Ray Strode 8b27c7
-        return min_uid <= uid && uid <= max_uid;
Ray Strode 8b27c7
-}
Ray Strode 8b27c7
 #endif /* ENABLE_USER_HEURISTICS */
Ray Strode 8b27c7
 
Ray Strode 8b27c7
 gboolean
Ray Strode 8b27c7
 user_classify_is_human (uid_t        uid,
Ray Strode 8b27c7
                         const gchar *username,
Ray Strode 8b27c7
                         const gchar *shell,
Ray Strode 8b27c7
                         const gchar *password_hash)
Ray Strode 8b27c7
 {
Ray Strode 8b27c7
+        if (user_classify_is_blacklisted (username))
Ray Strode 8b27c7
+                return FALSE;
Ray Strode 8b27c7
+
Ray Strode 8b27c7
 #ifdef ENABLE_USER_HEURISTICS
Ray Strode 8b27c7
-        return !user_classify_is_excluded_by_heuristics (username, shell, password_hash);
Ray Strode 8b27c7
-#else
Ray Strode 8b27c7
-        return user_classify_is_in_human_range (uid);
Ray Strode 8b27c7
+        /* only do heuristics on the range 500-1000 to catch one off migration problems in Fedora */
Ray Strode 8b27c7
+        if (uid >= 500 && uid < MINIMUM_UID) {
Ray Strode 8b27c7
+                if (!user_classify_is_excluded_by_heuristics (username, shell, password_hash))
Ray Strode 8b27c7
+                        return TRUE;
Ray Strode 8b27c7
+        }
Ray Strode 8b27c7
 #endif
Ray Strode 8b27c7
+
Ray Strode 8b27c7
+        return uid >= MINIMUM_UID;
Ray Strode 8b27c7
 }
Ray Strode 8b27c7
-- 
Ray Strode 8b27c7
1.8.4.2
Ray Strode 8b27c7