diff options
author | Ondrej Oprala | 2014-04-08 13:26:33 +0200 |
---|---|---|
committer | Karel Zak | 2014-05-15 13:55:59 +0200 |
commit | 78dd74508931a2990a20f973854e0749505fdea0 (patch) | |
tree | 19421097ccabbb7f14649764cfcdab552ac77677 /login-utils/logindefs.c | |
parent | lslogins: move to login-utils (diff) | |
download | kernel-qcow2-util-linux-78dd74508931a2990a20f973854e0749505fdea0.tar.gz kernel-qcow2-util-linux-78dd74508931a2990a20f973854e0749505fdea0.tar.xz kernel-qcow2-util-linux-78dd74508931a2990a20f973854e0749505fdea0.zip |
login: extract get_hushlogin_status
Signed-off-by: Ondrej Oprala <ooprala@redhat.com>
Diffstat (limited to 'login-utils/logindefs.c')
-rw-r--r-- | login-utils/logindefs.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/login-utils/logindefs.c b/login-utils/logindefs.c index 25f743b26..804ab3e31 100644 --- a/login-utils/logindefs.c +++ b/login-utils/logindefs.c @@ -27,6 +27,9 @@ #include <stdlib.h> #include <string.h> #include <sys/syslog.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <pwd.h> #include "c.h" #include "closestream.h" @@ -259,6 +262,125 @@ int logindefs_setenv(const char *name, const char *conf, const char *dflt) return val ? setenv(name, val, 1) : -1; } +/* + * We need to check the effective UID/GID. For example, $HOME could be on a + * root-squashed NFS or on an NFS with UID mapping, and access(2) uses the + * real UID/GID. Then open(2) seems as the surest solution. + * -- kzak@redhat.com (10-Apr-2009) + */ +int effective_access(const char *path, int mode) +{ + int fd = open(path, mode); + if (fd != -1) + close(fd); + return fd == -1 ? -1 : 0; +} + + +/* + * Check the per-account or the global hush-login setting. + * + * Hushed mode is enabled: + * + * a) if a 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 is found in the file + * + * b) if a ~/.hushlogin file exists + * + * The ~/.hushlogin file is ignored if the global hush file exists. + * + * The HUSHLOGIN_FILE login.def variable overrides the default hush filename. + * + * 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) + * + * + * The 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 privileges in the real uid. + * + * A portable solution would require a fork(), but we rely on Linux having the + * BSD setreuid(). + */ + +int get_hushlogin_status(struct passwd *pwd) +{ + const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL }; + const char *file; + char buf[BUFSIZ]; + int i; + + file = getlogindefs_str("HUSHLOGIN_FILE", NULL); + if (file) { + if (!*file) + return 0; /* empty HUSHLOGIN_FILE defined */ + + files[0] = file; + files[1] = NULL; + } + + for (i = 0; files[i]; i++) { + int ok = 0; + + file = files[i]; + + /* 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); + + if (setregid(-1, pwd->pw_gid) == 0 && + setreuid(0, pwd->pw_uid) == 0) + ok = effective_access(buf, O_RDONLY) == 0; + + if (setuid(0) != 0 || + setreuid(ruid, 0) != 0 || + setregid(-1, egid) != 0) { + syslog(LOG_ALERT, _("hush login status: restore original IDs failed")); + exit(EXIT_FAILURE); + } + if (ok) + return 1; /* enabled by user */ + } + } + + return 0; +} #ifdef TEST_PROGRAM int main(int argc, char *argv[]) { |