* src/userdel.c, libmisc/user_busy.c, libmisc/Makefile.am,
lib/prototypes.h: Move user_busy() to libmisc/user_busy.c. * NEWS, libmisc/user_busy.c: On Linux, do not check if an user is logged in with utmp, but check if the user is running some processes. If not on Linux, continue to search for an utmp record, but make sure the process recorded in the utmp entry is still running.
This commit is contained in:
parent
3e85eafb4c
commit
4a4549c49b
12
ChangeLog
12
ChangeLog
@ -1,4 +1,14 @@
|
||||
2009-05-17 Nicolas François <nicolas.francois@centraliens.net>
|
||||
2009-05-18 Nicolas François <nicolas.francois@centraliens.net>
|
||||
|
||||
* src/userdel.c, libmisc/user_busy.c, libmisc/Makefile.am,
|
||||
lib/prototypes.h: Move user_busy() to libmisc/user_busy.c.
|
||||
* NEWS, libmisc/user_busy.c: On Linux, do not check if an user is
|
||||
logged in with utmp, but check if the user is running some
|
||||
processes. If not on Linux, continue to search for an utmp record,
|
||||
but make sure the process recorded in the utmp entry is still
|
||||
running.
|
||||
|
||||
2009-05-18 Nicolas François <nicolas.francois@centraliens.net>
|
||||
|
||||
* man/usermod.8.xml: Document the -m/--move-home option.
|
||||
|
||||
|
5
NEWS
5
NEWS
@ -5,6 +5,11 @@ shadow-4.1.4 -> shadow-4.1.4.1 UNRELEASED
|
||||
- login
|
||||
* Fix failures with empty usernames on non PAM versions.
|
||||
* Fix CONSOLE (securetty) support on non PAM versions.
|
||||
- userdel
|
||||
* On Linux, do not check if an user is logged in with utmp, but check if
|
||||
the user is running some processes.
|
||||
* If not on Linux, continue to search for an utmp record, but make sure
|
||||
the process recorded in the utmp entry is still running.
|
||||
|
||||
shadow-4.1.3.1 -> shadow-4.1.4 2009-05-10
|
||||
|
||||
|
@ -363,6 +363,9 @@ extern char *tz (const char *);
|
||||
/* ulimit.c */
|
||||
extern int set_filesize_limit (int blocks);
|
||||
|
||||
/* user_busy.c */
|
||||
extern int user_busy (const char *name, uid_t uid);
|
||||
|
||||
/* utmp.c */
|
||||
extern /*@null@*/struct utmp *get_current_utmp (void);
|
||||
extern struct utmp *prepare_utmp (const char *name,
|
||||
|
@ -56,6 +56,7 @@ libmisc_a_SOURCES = \
|
||||
ttytype.c \
|
||||
tz.c \
|
||||
ulimit.c \
|
||||
user_busy.c \
|
||||
utmp.c \
|
||||
valid.c \
|
||||
xgetpwnam.c \
|
||||
|
226
libmisc/user_busy.c
Normal file
226
libmisc/user_busy.c
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 1991 - 1994, Julianne Frances Haugh
|
||||
* Copyright (c) 1996 - 2000, Marek Michałkiewicz
|
||||
* Copyright (c) 2000 - 2006, Tomasz Kłoczko
|
||||
* Copyright (c) 2007 - 2009, Nicolas François
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the copyright holders or contributors may not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ident "$Id: $"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include "defines.h"
|
||||
#include "prototypes.h"
|
||||
|
||||
#ifdef __linux__
|
||||
static int check_status (const char *sname, uid_t uid);
|
||||
static int user_busy_processes (uid_t uid);
|
||||
#else /* !__linux__ */
|
||||
static int user_busy_utmp (const char *name);
|
||||
#endif /* !__linux__ */
|
||||
|
||||
/*
|
||||
* user_busy - check if an user if currently running processes
|
||||
*/
|
||||
int user_busy (const char *name, uid_t uid)
|
||||
{
|
||||
/* There are no standard ways to get the list of processes.
|
||||
* An option could be to run an external tool (ps).
|
||||
*/
|
||||
#ifdef __linux__
|
||||
/* On Linux, directly parse /proc */
|
||||
return user_busy_processes (uid);
|
||||
#else /* !__linux__ */
|
||||
/* If we cannot rely on /proc, check is there is a record in utmp
|
||||
* indicating that the user is still logged in */
|
||||
return user_busy_utmp (name);
|
||||
#endif /* !__linux__ */
|
||||
}
|
||||
|
||||
#ifndef __linux__
|
||||
static int user_busy_utmp (const char *name)
|
||||
{
|
||||
#ifdef USE_UTMPX
|
||||
struct utmpx *utent;
|
||||
|
||||
setutxent ();
|
||||
while ((utent = getutxent ()) != NULL)
|
||||
#else /* !USE_UTMPX */
|
||||
struct utmp *utent;
|
||||
|
||||
setutent ();
|
||||
while ((utent = getutent ()) != NULL)
|
||||
#endif /* !USE_UTMPX */
|
||||
{
|
||||
if (utent->ut_type != USER_PROCESS) {
|
||||
continue;
|
||||
}
|
||||
if (strncmp (utent->ut_user, name, sizeof utent->ut_user) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (kill (utent->ut_pid, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return USER_BUSY;
|
||||
}
|
||||
}
|
||||
#endif /* !__linux__ */
|
||||
|
||||
#ifdef __linux__
|
||||
static int check_status (const char *sname, uid_t uid)
|
||||
{
|
||||
/* 40: /proc/xxxxxxxxxx/task/xxxxxxxxxx/status + \0 */
|
||||
char status[40];
|
||||
char line[1024];
|
||||
FILE *sfile;
|
||||
|
||||
snprintf (status, 40, "/proc/%s/status", sname);
|
||||
status[39] = '\0';
|
||||
|
||||
sfile = fopen (status, "r");
|
||||
if (NULL == sfile) {
|
||||
return 0;
|
||||
}
|
||||
while (fgets (line, sizeof (line), sfile) == line) {
|
||||
if (strncmp (line, "Uid:\t", 5) == 0) {
|
||||
unsigned long ruid, euid, suid;
|
||||
assert (uid == (unsigned long) uid);
|
||||
if (sscanf (line,
|
||||
"Uid:\t%lu\t%lu\t%lu\n",
|
||||
&ruid, &euid, &suid) == 3) {
|
||||
if ( (ruid == (unsigned long) uid)
|
||||
|| (euid == (unsigned long) uid)
|
||||
|| (suid == (unsigned long) uid)) {
|
||||
(void) fclose (sfile);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* Ignore errors. This is just a best effort. */
|
||||
}
|
||||
(void) fclose (sfile);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
(void) fclose (sfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_busy_processes (uid_t uid)
|
||||
{
|
||||
DIR *proc;
|
||||
struct dirent *ent;
|
||||
char *tmp_d_name;
|
||||
pid_t pid;
|
||||
DIR *task_dir;
|
||||
/* 22: /proc/xxxxxxxxxx/task + \0 */
|
||||
char task_path[22];
|
||||
char root_path[22];
|
||||
struct stat sbroot;
|
||||
struct stat sbroot_process;
|
||||
|
||||
proc = opendir ("/proc");
|
||||
if (proc == NULL) {
|
||||
perror ("opendir /proc");
|
||||
return 0;
|
||||
}
|
||||
if (stat ("/", &sbroot) != 0) {
|
||||
perror ("stat (\"/\")");
|
||||
(void) closedir (proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((ent = readdir (proc)) != NULL) {
|
||||
tmp_d_name = ent->d_name;
|
||||
/*
|
||||
* Ingo Molnar's patch introducing NPTL for 2.4 hides
|
||||
* threads in the /proc directory by prepending a period.
|
||||
* This patch is applied by default in some RedHat
|
||||
* kernels.
|
||||
*/
|
||||
if ( (strcmp (tmp_d_name, ".") == 0)
|
||||
|| (strcmp (tmp_d_name, "..") == 0)) {
|
||||
continue;
|
||||
}
|
||||
if (*tmp_d_name == '.') {
|
||||
tmp_d_name++;
|
||||
}
|
||||
|
||||
/* Check if this is a valid PID */
|
||||
if (get_pid (tmp_d_name, &pid) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if the process is in our chroot */
|
||||
snprintf (root_path, 22, "/proc/%lu/root", (unsigned long) pid);
|
||||
root_path[21] = '\0';
|
||||
if (stat (root_path, &sbroot_process) != 0) {
|
||||
continue;
|
||||
}
|
||||
if ( (sbroot.st_dev != sbroot_process.st_dev)
|
||||
|| (sbroot.st_ino != sbroot_process.st_ino)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (check_status (tmp_d_name, uid) != 0) {
|
||||
(void) closedir (proc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf (task_path, 22, "/proc/%lu/task", (unsigned long) pid);
|
||||
task_path[21] = '\0';
|
||||
task_dir = opendir (task_path);
|
||||
if (task_dir != NULL) {
|
||||
while ((ent = readdir (task_dir)) != NULL) {
|
||||
pid_t tid;
|
||||
if (get_pid (ent->d_name, &tid) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (tid == pid) {
|
||||
continue;
|
||||
}
|
||||
if (check_status (task_path+6, uid) != 0) {
|
||||
(void) closedir (proc);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
(void) closedir (task_dir);
|
||||
} else {
|
||||
/* Ignore errors. This is just a best effort */
|
||||
}
|
||||
}
|
||||
|
||||
(void) closedir (proc);
|
||||
return 0;
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
@ -100,7 +100,6 @@ static void close_files (void);
|
||||
static void fail_exit (int);
|
||||
static void open_files (void);
|
||||
static void update_user (void);
|
||||
static void user_busy (const char *, uid_t);
|
||||
static void user_cancel (const char *);
|
||||
|
||||
#ifdef EXTRA_CHECK_HOME_DIR
|
||||
@ -576,57 +575,6 @@ static void update_user (void)
|
||||
SYSLOG ((LOG_INFO, "delete user '%s'\n", user_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* user_busy - see if user is logged in.
|
||||
*
|
||||
* XXX - should probably check if there are any processes owned
|
||||
* by this user. Also, I think this check should be in usermod
|
||||
* as well (at least when changing username or UID). --marekm
|
||||
*/
|
||||
static void user_busy (const char *name, uid_t uid)
|
||||
{
|
||||
|
||||
/*
|
||||
* We see if the user is logged in by looking for the user name
|
||||
* in the utmp file.
|
||||
*/
|
||||
#ifdef USE_UTMPX
|
||||
struct utmpx *utent;
|
||||
|
||||
setutxent ();
|
||||
while ((utent = getutxent ()) != NULL)
|
||||
#else /* !USE_UTMPX */
|
||||
struct utmp *utent;
|
||||
|
||||
setutent ();
|
||||
while ((utent = getutent ()) != NULL)
|
||||
#endif /* !USE_UTMPX */
|
||||
{
|
||||
if (utent->ut_type != USER_PROCESS) {
|
||||
continue;
|
||||
}
|
||||
if (strncmp (utent->ut_user, name, sizeof utent->ut_user) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (kill (utent->ut_pid, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf (stderr,
|
||||
_("%s: user %s is currently logged in\n"),
|
||||
Prog, name);
|
||||
if (!fflg) {
|
||||
#ifdef WITH_AUDIT
|
||||
audit_logger (AUDIT_DEL_USER, Prog,
|
||||
"deleting user logged in",
|
||||
name, AUDIT_NO_ID,
|
||||
SHADOW_AUDIT_FAILURE);
|
||||
#endif
|
||||
exit (E_USER_BUSY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* user_cancel - cancel cron and at jobs
|
||||
*
|
||||
@ -648,7 +596,7 @@ static void user_cancel (const char *user)
|
||||
if (pid == 0) {
|
||||
execl (cmd, cmd, user, (char *) 0);
|
||||
perror (cmd);
|
||||
_exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
|
||||
exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
|
||||
} else if ((pid_t)-1 == pid) {
|
||||
perror ("fork");
|
||||
return;
|
||||
@ -893,8 +841,23 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
/*
|
||||
* Check to make certain the user isn't logged in.
|
||||
* Note: This is a best effort basis. The user may log in between,
|
||||
* a cron job may be started on her behalf, etc.
|
||||
*/
|
||||
user_busy (user_name, user_id);
|
||||
if (user_busy (user_name, user_id) != 0) {
|
||||
fprintf (stderr,
|
||||
_("%s: user %s is currently logged in\n"),
|
||||
Prog, user_name);
|
||||
if (!fflg) {
|
||||
#ifdef WITH_AUDIT
|
||||
audit_logger (AUDIT_DEL_USER, Prog,
|
||||
"deleting user logged in",
|
||||
user_name, AUDIT_NO_ID,
|
||||
SHADOW_AUDIT_FAILURE);
|
||||
#endif
|
||||
exit (E_USER_BUSY);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the hard stuff - open the files, create the user entries,
|
||||
|
Loading…
Reference in New Issue
Block a user