chiark / gitweb /
core: add RemoveIPC= setting
[elogind.git] / src / shared / clean-ipc.c
index 39ab645..4e64d37 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <mqueue.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
 #include <sys/ipc.h>
-#include <sys/shm.h>
-#include <sys/sem.h>
 #include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
 #include <sys/stat.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <mqueue.h>
+#include <unistd.h>
 
-#include "util.h"
-#include "strv.h"
 #include "clean-ipc.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+
+static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
+
+        if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
+                return true;
+
+        if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
+                return true;
+
+        return false;
+}
 
-static int clean_sysvipc_shm(uid_t delete_uid) {
+static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -44,8 +65,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
                 if (errno == ENOENT)
                         return 0;
 
-                log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
-                return -errno;
+                return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
         }
 
         FOREACH_LINE(line, f, goto fail) {
@@ -69,7 +89,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
                 if (n_attached > 0)
                         continue;
 
-                if (uid != delete_uid)
+                if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
                 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
@@ -78,19 +98,19 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
                         if (errno == EIDRM || errno == EINVAL)
                                 continue;
 
-                        log_warning_errno(errno, "Failed to remove SysV shared memory segment %i: %m", shmid);
-                        ret = -errno;
+                        ret = log_warning_errno(errno,
+                                                "Failed to remove SysV shared memory segment %i: %m",
+                                                shmid);
                 }
         }
 
         return ret;
 
 fail:
-        log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
-        return -errno;
+        return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
 }
 
-static int clean_sysvipc_sem(uid_t delete_uid) {
+static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -101,8 +121,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
                 if (errno == ENOENT)
                         return 0;
 
-                log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
-                return -errno;
+                return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
         }
 
         FOREACH_LINE(line, f, goto fail) {
@@ -121,7 +140,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
                            &semid, &uid, &gid, &cuid, &cgid) != 5)
                         continue;
 
-                if (uid != delete_uid)
+                if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
                 if (semctl(semid, 0, IPC_RMID) < 0) {
@@ -130,19 +149,19 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
                         if (errno == EIDRM || errno == EINVAL)
                                 continue;
 
-                        log_warning_errno(errno, "Failed to remove SysV semaphores object %i: %m", semid);
-                        ret = -errno;
+                        ret = log_warning_errno(errno,
+                                                "Failed to remove SysV semaphores object %i: %m",
+                                                semid);
                 }
         }
 
         return ret;
 
 fail:
-        log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
-        return -errno;
+        return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
 }
 
-static int clean_sysvipc_msg(uid_t delete_uid) {
+static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
         _cleanup_fclose_ FILE *f = NULL;
         char line[LINE_MAX];
         bool first = true;
@@ -153,8 +172,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
                 if (errno == ENOENT)
                         return 0;
 
-                log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
-                return -errno;
+                return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
         }
 
         FOREACH_LINE(line, f, goto fail) {
@@ -174,7 +192,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
                            &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
                         continue;
 
-                if (uid != delete_uid)
+                if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
                         continue;
 
                 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
@@ -183,25 +201,25 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
                         if (errno == EIDRM || errno == EINVAL)
                                 continue;
 
-                        log_warning_errno(errno, "Failed to remove SysV message queue %i: %m", msgid);
-                        ret = -errno;
+                        ret = log_warning_errno(errno,
+                                                "Failed to remove SysV message queue %i: %m",
+                                                msgid);
                 }
         }
 
         return ret;
 
 fail:
-        log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
-        return -errno;
+        return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
 }
 
-static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
+static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
         struct dirent *de;
         int ret = 0, r;
 
         assert(dir);
 
-        FOREACH_DIRENT(de, dir, goto fail) {
+        FOREACH_DIRENT_ALL(de, dir, goto fail) {
                 struct stat st;
 
                 if (STR_IN_SET(de->d_name, "..", "."))
@@ -211,12 +229,11 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
                         if (errno == ENOENT)
                                 continue;
 
-                        log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
-                        ret = -errno;
+                        ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
                         continue;
                 }
 
-                if (st.st_uid != uid)
+                if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
                         continue;
 
                 if (S_ISDIR(st.st_mode)) {
@@ -224,12 +241,10 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
 
                         kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
                         if (!kid) {
-                                if (errno != ENOENT) {
-                                        log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
-                                        ret = -errno;
-                                }
+                                if (errno != ENOENT)
+                                        ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
                         } else {
-                                r = clean_posix_shm_internal(kid, uid);
+                                r = clean_posix_shm_internal(kid, uid, gid);
                                 if (r < 0)
                                         ret = r;
                         }
@@ -239,8 +254,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
                                 if (errno == ENOENT)
                                         continue;
 
-                                log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
-                                ret = -errno;
+                                ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
                         }
                 } else {
 
@@ -249,8 +263,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
                                 if (errno == ENOENT)
                                         continue;
 
-                                log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
-                                ret = -errno;
+                                ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
                         }
                 }
         }
@@ -258,11 +271,10 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
         return ret;
 
 fail:
-        log_warning_errno(errno, "Failed to read /dev/shm: %m");
-        return -errno;
+        return log_warning_errno(errno, "Failed to read /dev/shm: %m");
 }
 
-static int clean_posix_shm(uid_t uid) {
+static int clean_posix_shm(uid_t uid, gid_t gid) {
         _cleanup_closedir_ DIR *dir = NULL;
 
         dir = opendir("/dev/shm");
@@ -270,14 +282,14 @@ static int clean_posix_shm(uid_t uid) {
                 if (errno == ENOENT)
                         return 0;
 
-                log_warning_errno(errno, "Failed to open /dev/shm: %m");
-                return -errno;
+                return log_warning_errno(errno, "Failed to open /dev/shm: %m");
         }
 
-        return clean_posix_shm_internal(dir, uid);
+        return clean_posix_shm_internal(dir, uid, gid);
 }
 
-static int clean_posix_mq(uid_t uid) {
+#if 0 /// UNNEEDED by elogind
+static int clean_posix_mq(uid_t uid, gid_t gid) {
         _cleanup_closedir_ DIR *dir = NULL;
         struct dirent *de;
         int ret = 0;
@@ -287,11 +299,10 @@ static int clean_posix_mq(uid_t uid) {
                 if (errno == ENOENT)
                         return 0;
 
-                log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
-                return -errno;
+                return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
         }
 
-        FOREACH_DIRENT(de, dir, goto fail) {
+        FOREACH_DIRENT_ALL(de, dir, goto fail) {
                 struct stat st;
                 char fn[1+strlen(de->d_name)+1];
 
@@ -302,12 +313,13 @@ static int clean_posix_mq(uid_t uid) {
                         if (errno == ENOENT)
                                 continue;
 
-                        log_warning_errno(errno, "Failed to stat() MQ segment %s: %m", de->d_name);
-                        ret = -errno;
+                        ret = log_warning_errno(errno,
+                                                "Failed to stat() MQ segment %s: %m",
+                                                de->d_name);
                         continue;
                 }
 
-                if (st.st_uid != uid)
+                if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
                         continue;
 
                 fn[0] = '/';
@@ -317,44 +329,59 @@ static int clean_posix_mq(uid_t uid) {
                         if (errno == ENOENT)
                                 continue;
 
-                        log_warning_errno(errno, "Failed to unlink POSIX message queue %s: %m", fn);
-                        ret = -errno;
+                        ret = log_warning_errno(errno,
+                                                "Failed to unlink POSIX message queue %s: %m",
+                                                fn);
                 }
         }
 
         return ret;
 
 fail:
-        log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
-        return -errno;
+        return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
 }
+#endif // 0
 
-int clean_ipc(uid_t uid) {
+int clean_ipc(uid_t uid, gid_t gid) {
         int ret = 0, r;
 
-        /* Refuse to clean IPC of the root and system users */
-        if (uid <= SYSTEM_UID_MAX)
+        /* Anything to do? */
+        if (!uid_is_valid(uid) && !gid_is_valid(gid))
                 return 0;
 
-        r = clean_sysvipc_shm(uid);
+        /* Refuse to clean IPC of the root user */
+        if (uid == 0 && gid == 0)
+                return 0;
+
+        r = clean_sysvipc_shm(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_sysvipc_sem(uid);
+        r = clean_sysvipc_sem(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_sysvipc_msg(uid);
+        r = clean_sysvipc_msg(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_posix_shm(uid);
+        r = clean_posix_shm(uid, gid);
         if (r < 0)
                 ret = r;
 
-        r = clean_posix_mq(uid);
+#if 0 /// elogind does not use mq_open anywhere
+        r = clean_posix_mq(uid, gid);
         if (r < 0)
                 ret = r;
+#endif // 0
 
         return ret;
 }
+
+int clean_ipc_by_uid(uid_t uid) {
+        return clean_ipc(uid, GID_INVALID);
+}
+
+int clean_ipc_by_gid(gid_t gid) {
+        return clean_ipc(UID_INVALID, gid);
+}