Do not mistake a regular user process for a namespaced one

In case there is a regular user with a process running on a system
with uid falling into a namespaced uid range of another user.
The user with the colliding namespaced uid range will not be
allowed to be deleted without forcing the action with -f.

The user_busy() is adjusted to check whether the suspected process
is really a namespaced process in a different namespace.
This commit is contained in:
Tomas Mraz 2020-01-20 13:58:07 +01:00
parent 3e942adb21
commit fd4405b763

View File

@ -39,6 +39,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include "defines.h" #include "defines.h"
#include "prototypes.h" #include "prototypes.h"
#ifdef ENABLE_SUBIDS #ifdef ENABLE_SUBIDS
@ -106,6 +107,31 @@ static int user_busy_utmp (const char *name)
#endif /* !__linux__ */ #endif /* !__linux__ */
#ifdef __linux__ #ifdef __linux__
#ifdef ENABLE_SUBIDS
#define in_parentuid_range(uid) ((uid) >= parentuid && (uid) < parentuid + range)
static int different_namespace (const char *sname)
{
/* 41: /proc/xxxxxxxxxx/task/xxxxxxxxxx/ns/user + \0 */
char path[41];
char buf[512], buf2[512];
ssize_t llen1, llen2;
snprintf (path, 41, "/proc/%s/ns/user", sname);
if ((llen1 = readlink (path, buf, sizeof(buf))) == -1)
return 0;
if ((llen2 = readlink ("/proc/self/ns/user", buf2, sizeof(buf2))) == -1)
return 0;
if (llen1 == llen2 && memcmp (buf, buf2, llen1) == 0)
return 0; /* same namespace */
return 1;
}
#endif /* ENABLE_SUBIDS */
static int check_status (const char *name, const char *sname, uid_t uid) static int check_status (const char *name, const char *sname, uid_t uid)
{ {
/* 40: /proc/xxxxxxxxxx/task/xxxxxxxxxx/status + \0 */ /* 40: /proc/xxxxxxxxxx/task/xxxxxxxxxx/status + \0 */
@ -114,7 +140,6 @@ static int check_status (const char *name, const char *sname, uid_t uid)
FILE *sfile; FILE *sfile;
snprintf (status, 40, "/proc/%s/status", sname); snprintf (status, 40, "/proc/%s/status", sname);
status[39] = '\0';
sfile = fopen (status, "r"); sfile = fopen (status, "r");
if (NULL == sfile) { if (NULL == sfile) {
@ -123,26 +148,29 @@ static int check_status (const char *name, const char *sname, uid_t uid)
while (fgets (line, sizeof (line), sfile) == line) { while (fgets (line, sizeof (line), sfile) == line) {
if (strncmp (line, "Uid:\t", 5) == 0) { if (strncmp (line, "Uid:\t", 5) == 0) {
unsigned long ruid, euid, suid; unsigned long ruid, euid, suid;
assert (uid == (unsigned long) uid); assert (uid == (unsigned long) uid);
(void) fclose (sfile);
if (sscanf (line, if (sscanf (line,
"Uid:\t%lu\t%lu\t%lu\n", "Uid:\t%lu\t%lu\t%lu\n",
&ruid, &euid, &suid) == 3) { &ruid, &euid, &suid) == 3) {
if ( (ruid == (unsigned long) uid) if ( (ruid == (unsigned long) uid)
|| (euid == (unsigned long) uid) || (euid == (unsigned long) uid)
|| (suid == (unsigned long) uid) || (suid == (unsigned long) uid) ) {
#ifdef ENABLE_SUBIDS
|| have_sub_uids(name, ruid, 1)
|| have_sub_uids(name, euid, 1)
|| have_sub_uids(name, suid, 1)
#endif /* ENABLE_SUBIDS */
) {
(void) fclose (sfile);
return 1; return 1;
} }
#ifdef ENABLE_SUBIDS
if ( different_namespace (sname)
&& ( have_sub_uids(name, ruid, 1)
|| have_sub_uids(name, euid, 1)
|| have_sub_uids(name, suid, 1))
) {
return 1;
}
#endif /* ENABLE_SUBIDS */
} else { } else {
/* Ignore errors. This is just a best effort. */ /* Ignore errors. This is just a best effort. */
} }
(void) fclose (sfile);
return 0; return 0;
} }
} }