login: extract get_hushlogin_status
Signed-off-by: Ondrej Oprala <ooprala@redhat.com>
This commit is contained in:
parent
914e20a00a
commit
78dd745089
|
@ -920,124 +920,6 @@ static void loginpam_session(struct login_context *cxt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
*/
|
|
||||||
static 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().
|
|
||||||
*/
|
|
||||||
static 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detach the controlling terminal, fork, restore syslog stuff, and create
|
* Detach the controlling terminal, fork, restore syslog stuff, and create
|
||||||
* a new session.
|
* a new session.
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/syslog.h>
|
#include <sys/syslog.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
#include "c.h"
|
#include "c.h"
|
||||||
#include "closestream.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;
|
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
|
#ifdef TEST_PROGRAM
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,5 +8,7 @@ extern unsigned long getlogindefs_num(const char *name, long dflt);
|
||||||
extern const char *getlogindefs_str(const char *name, const char *dflt);
|
extern const char *getlogindefs_str(const char *name, const char *dflt);
|
||||||
extern void free_getlogindefs_data(void);
|
extern void free_getlogindefs_data(void);
|
||||||
extern int logindefs_setenv(const char *name, const char *conf, const char *dflt);
|
extern int logindefs_setenv(const char *name, const char *conf, const char *dflt);
|
||||||
|
extern int effective_access(const char *path, int mode);
|
||||||
|
extern int get_hushlogin_status(struct passwd *pwd);
|
||||||
|
|
||||||
#endif /* UTIL_LINUX_LOGINDEFS_H */
|
#endif /* UTIL_LINUX_LOGINDEFS_H */
|
||||||
|
|
Loading…
Reference in New Issue