]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
login: improve hushed mode (merge suse changes)
authorKarel Zak <kzak@redhat.com>
Mon, 29 Aug 2011 08:30:42 +0000 (10:30 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 26 Oct 2011 21:17:16 +0000 (23:17 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
include/pathnames.h
login-utils/login.c

index 025e4afa91df8cb60f3ddf0da5a709a51afdf37d..1a54a037d9dd5645ed3cae94836d5b5fcd84edac 100644 (file)
@@ -29,6 +29,7 @@
 #define _PATH_WTMPLOCK         "/etc/wtmplock"
 
 #define        _PATH_HUSHLOGIN         ".hushlogin"
+#define        _PATH_HUSHLOGINS        "/etc/hushlogins"
 
 #ifndef _PATH_MAILDIR
 #define        _PATH_MAILDIR           "/var/spool/mail"
index a735bc29c232dd0f42dfd8d4d3b6b4cb1b0114cb..38b17f5bfbdae43e0d2d14b365b6fcfb80d30766 100644 (file)
@@ -566,7 +566,7 @@ static int loginpam_err(pam_handle_t * pamh, int retcode)
  * The open(2) seems as the surest solution.
  * -- kzak@redhat.com (10-Apr-2009)
  */
-int effective_access(const char *path, int mode)
+static int effective_access(const char *path, int mode)
 {
        int fd = open(path, mode);
        if (fd != -1)
@@ -574,6 +574,94 @@ int effective_access(const char *path, int mode)
        return fd == -1 ? -1 : 0;
 }
 
+/*
+ * Check per accout or global hush-login setting.
+ *
+ * Hushed mode is enabled:
+ *
+ * a) if global (e.g. /etc/hushlogins) hush file exists:
+ *     1) for ALL ACCOUNTS if the file is empty
+ *     2) for the current user if the username or shell are found in the file
+ *
+ * b) if ~/.hushlogin file exists
+ *
+ * The ~/.hushlogin is ignored if the global hush file exists.
+ *
+ * Note that shadow-utils login(1) does not support "a1)". The "a1)" is
+ * necessary if you want to use PAM for "Last login" message.
+ *
+ * -- Karel Zak <kzak@redhat.com> (26-Aug-2011)
+ *
+ *
+ * Per-account check requires some explanation: As root we may not be able to
+ * read the directory of the user if it is on an NFS mounted filesystem. We
+ * temporarily set our effective uid to the user-uid making sure that we keep
+ * root privs. in the real uid.
+ *
+ * A portable solution would require a fork(), but we rely on Linux having the
+ * BSD setreuid()
+ */
+static int get_hushlogin_status(struct passwd *pwd)
+{
+       const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL };
+       char buf[BUFSIZ];
+       int i;
+
+       for (i = 0; files[i]; i++) {
+               const char *file = files[i];
+               int ok = 0;
+
+               /* Global hush-file*/
+               if (*file == '/') {
+                       struct stat st;
+                       FILE *f;
+
+                       if (stat(file, &st) != 0)
+                               continue;       /* file does not exist */
+
+                       if (st.st_size == 0)
+                               return 1;       /* for all accounts */
+
+                       f = fopen(file, "r");
+                       if (!f)
+                               continue;       /* ignore errors... */
+
+                       while (ok == 0 && fgets(buf, sizeof(buf), f)) {
+                               buf[strlen(buf) - 1] = '\0';
+                               ok = !strcmp(buf, *buf == '/' ? pwd->pw_shell :
+                                                               pwd->pw_name);
+                       }
+                       fclose(f);
+                       if (ok)
+                               return 1;       /* found username/shell */
+
+                       return 0;               /* ignore per-account files */
+               }
+
+               /* Per-account setting */
+               if (strlen(pwd->pw_dir) + sizeof(file) + 2 > sizeof(buf))
+                       continue;
+               else {
+                       uid_t ruid = getuid();
+                       gid_t egid = getegid();
+
+                       sprintf(buf, "%s/%s", pwd->pw_dir, file);
+                       setregid(-1, pwd->pw_gid);
+                       setreuid(0, pwd->pw_uid);
+                       ok = effective_access(buf, O_RDONLY) == 0;
+                       setuid(0);      /* setreuid doesn't do it alone! */
+                       setreuid(ruid, 0);
+                       setregid(-1, egid);
+
+                       if (ok)
+                               return 1;       /* enabled by user */
+               }
+       }
+
+       return 0;
+}
+
+
 int main(int argc, char **argv)
 {
        extern int optind;
@@ -879,37 +967,7 @@ int main(int argc, char **argv)
 
        endpwent();
 
-       {
-               /*
-                * Check per accout setting.
-                *
-                * This requires some explanation: As root we may not be able to
-                * read the directory of the user if it is on an NFS mounted
-                * filesystem. We temporarily set our effective uid to the user-uid
-                * making sure that we keep root privs. in the real uid.
-                *
-                * A portable solution would require a fork(), but we rely on Linux
-                * having the BSD setreuid()
-                */
-               char tmpstr[PATH_MAX];
-               uid_t ruid = getuid();
-               gid_t egid = getegid();
-
-               /* avoid snprintf - old systems do not have it, or worse,
-                  have a libc in which snprintf is the same as sprintf */
-               if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 >
-                   PATH_MAX)
-                       cxt.quiet = 0;
-               else {
-                       sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
-                       setregid(-1, pwd->pw_gid);
-                       setreuid(0, pwd->pw_uid);
-                       cxt.quiet = (effective_access(tmpstr, O_RDONLY) == 0);
-                       setuid(0);      /* setreuid doesn't do it alone! */
-                       setreuid(ruid, 0);
-                       setregid(-1, egid);
-               }
-       }
+       cxt.quiet = get_hushlogin_status(pwd);
 
        log_utmp(&cxt);
        log_audit(&cxt, 1);