Blame SOURCES/fix-user-classification-logic.patch

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