/* * Copyright (C) 2009-2010 Red Hat, Inc. * Copyright (C) 2013 Canonical Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the licence, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Ryan Lortie * Matthias Clasen */ #include "config.h" #include "user-classify.h" #include #include static const char *default_excludes[] = { "bin", "root", "daemon", "adm", "lp", "sync", "shutdown", "halt", "mail", "news", "uucp", "nobody", "postgres", "pvm", "rpm", "nfsnobody", "pcap", "mysql", "ftp", "games", "man", "at", "gdm", "gnome-initial-setup" }; static gboolean user_classify_is_blacklisted (const char *username) { static GHashTable *exclusions; if (exclusions == NULL) { guint i; exclusions = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < G_N_ELEMENTS (default_excludes); i++) { g_hash_table_add (exclusions, (gpointer) default_excludes[i]); } } if (g_hash_table_contains (exclusions, username)) { return TRUE; } return FALSE; } #ifdef ENABLE_USER_HEURISTICS static gboolean user_classify_is_excluded_by_heuristics (const gchar *username, const gchar *password_hash) { gboolean ret = FALSE; 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; } } } return ret; } #endif /* ENABLE_USER_HEURISTICS */ static gboolean is_invalid_shell (const char *shell) { g_autofree gchar *basename = NULL; int ret = FALSE; #ifdef HAVE_GETUSERSHELL /* getusershell returns a whitelist of valid shells. * assume the shell is invalid unless there is a match */ ret = TRUE; char *valid_shell; setusershell (); while ((valid_shell = getusershell ()) != NULL) { if (g_strcmp0 (shell, valid_shell) != 0) continue; ret = FALSE; break; } endusershell (); #endif /* always check for false and nologin since they are sometimes included by getusershell */ basename = g_path_get_basename (shell); if (shell[0] == '\0') { return TRUE; } else if (g_strcmp0 (basename, "nologin") == 0) { return TRUE; } else if (g_strcmp0 (basename, "false") == 0) { return TRUE; } return ret; } gboolean user_classify_is_human (uid_t uid, const gchar *username, const gchar *shell, const gchar *password_hash) { if (user_classify_is_blacklisted (username)) return FALSE; if (shell != NULL && is_invalid_shell (shell)) return FALSE; #ifdef ENABLE_USER_HEURISTICS /* only do heuristics on the range 500-1000 to catch one off migration problems in Fedora */ if (uid >= 500 && uid < MINIMUM_UID) { if (!user_classify_is_excluded_by_heuristics (username, password_hash)) return TRUE; } #endif return uid >= MINIMUM_UID; }