LCOV - code coverage report
Current view: top level - shared - clean-ipc.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 240 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 10 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <dirent.h>
       4             : #include <errno.h>
       5             : #include <fcntl.h>
       6             : #include <limits.h>
       7             : #include <mqueue.h>
       8             : #include <stdbool.h>
       9             : #include <stdio.h>
      10             : #include <string.h>
      11             : #include <sys/ipc.h>
      12             : #include <sys/msg.h>
      13             : #include <sys/sem.h>
      14             : #include <sys/shm.h>
      15             : #include <sys/stat.h>
      16             : #include <unistd.h>
      17             : 
      18             : #include "clean-ipc.h"
      19             : #include "dirent-util.h"
      20             : #include "fd-util.h"
      21             : #include "fileio.h"
      22             : #include "format-util.h"
      23             : #include "log.h"
      24             : #include "macro.h"
      25             : #include "string-util.h"
      26             : #include "strv.h"
      27             : #include "user-util.h"
      28             : 
      29           0 : static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
      30             : 
      31           0 :         if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
      32           0 :                 return true;
      33             : 
      34           0 :         if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
      35           0 :                 return true;
      36             : 
      37           0 :         return false;
      38             : }
      39             : 
      40           0 : static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) {
      41           0 :         _cleanup_fclose_ FILE *f = NULL;
      42           0 :         bool first = true;
      43           0 :         int ret = 0, r;
      44             : 
      45           0 :         f = fopen("/proc/sysvipc/shm", "re");
      46           0 :         if (!f) {
      47           0 :                 if (errno == ENOENT)
      48           0 :                         return 0;
      49             : 
      50           0 :                 return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
      51             :         }
      52             : 
      53           0 :         for (;;) {
      54           0 :                 _cleanup_free_ char *line = NULL;
      55             :                 unsigned n_attached;
      56             :                 pid_t cpid, lpid;
      57             :                 uid_t uid, cuid;
      58             :                 gid_t gid, cgid;
      59             :                 int shmid;
      60             : 
      61           0 :                 r = read_line(f, LONG_LINE_MAX, &line);
      62           0 :                 if (r < 0)
      63           0 :                         return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
      64           0 :                 if (r == 0)
      65           0 :                         break;
      66             : 
      67           0 :                 if (first) {
      68           0 :                         first = false;
      69           0 :                         continue;
      70             :                 }
      71             : 
      72           0 :                 if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
      73             :                            &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
      74           0 :                         continue;
      75             : 
      76           0 :                 if (n_attached > 0)
      77           0 :                         continue;
      78             : 
      79           0 :                 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
      80           0 :                         continue;
      81             : 
      82           0 :                 if (!rm)
      83           0 :                         return 1;
      84             : 
      85           0 :                 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
      86             : 
      87             :                         /* Ignore entries that are already deleted */
      88           0 :                         if (IN_SET(errno, EIDRM, EINVAL))
      89           0 :                                 continue;
      90             : 
      91           0 :                         ret = log_warning_errno(errno,
      92             :                                                 "Failed to remove SysV shared memory segment %i: %m",
      93             :                                                 shmid);
      94             :                 } else {
      95           0 :                         log_debug("Removed SysV shared memory segment %i.", shmid);
      96           0 :                         if (ret == 0)
      97           0 :                                 ret = 1;
      98             :                 }
      99             :         }
     100             : 
     101           0 :         return ret;
     102             : }
     103             : 
     104           0 : static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid, bool rm) {
     105           0 :         _cleanup_fclose_ FILE *f = NULL;
     106           0 :         bool first = true;
     107           0 :         int ret = 0, r;
     108             : 
     109           0 :         f = fopen("/proc/sysvipc/sem", "re");
     110           0 :         if (!f) {
     111           0 :                 if (errno == ENOENT)
     112           0 :                         return 0;
     113             : 
     114           0 :                 return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
     115             :         }
     116             : 
     117           0 :         for (;;) {
     118           0 :                 _cleanup_free_ char *line = NULL;
     119             :                 uid_t uid, cuid;
     120             :                 gid_t gid, cgid;
     121             :                 int semid;
     122             : 
     123           0 :                 r = read_line(f, LONG_LINE_MAX, &line);
     124           0 :                 if (r < 0)
     125           0 :                         return log_warning_errno(r, "Failed to read /proc/sysvipc/sem: %m");
     126           0 :                 if (r == 0)
     127           0 :                         break;
     128             : 
     129           0 :                 if (first) {
     130           0 :                         first = false;
     131           0 :                         continue;
     132             :                 }
     133             : 
     134           0 :                 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
     135             :                            &semid, &uid, &gid, &cuid, &cgid) != 5)
     136           0 :                         continue;
     137             : 
     138           0 :                 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
     139           0 :                         continue;
     140             : 
     141           0 :                 if (!rm)
     142           0 :                         return 1;
     143             : 
     144           0 :                 if (semctl(semid, 0, IPC_RMID) < 0) {
     145             : 
     146             :                         /* Ignore entries that are already deleted */
     147           0 :                         if (IN_SET(errno, EIDRM, EINVAL))
     148           0 :                                 continue;
     149             : 
     150           0 :                         ret = log_warning_errno(errno,
     151             :                                                 "Failed to remove SysV semaphores object %i: %m",
     152             :                                                 semid);
     153             :                 } else {
     154           0 :                         log_debug("Removed SysV semaphore %i.", semid);
     155           0 :                         if (ret == 0)
     156           0 :                                 ret = 1;
     157             :                 }
     158             :         }
     159             : 
     160           0 :         return ret;
     161             : }
     162             : 
     163           0 : static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid, bool rm) {
     164           0 :         _cleanup_fclose_ FILE *f = NULL;
     165           0 :         bool first = true;
     166           0 :         int ret = 0, r;
     167             : 
     168           0 :         f = fopen("/proc/sysvipc/msg", "re");
     169           0 :         if (!f) {
     170           0 :                 if (errno == ENOENT)
     171           0 :                         return 0;
     172             : 
     173           0 :                 return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
     174             :         }
     175             : 
     176           0 :         for (;;) {
     177           0 :                 _cleanup_free_ char *line = NULL;
     178             :                 uid_t uid, cuid;
     179             :                 gid_t gid, cgid;
     180             :                 pid_t cpid, lpid;
     181             :                 int msgid;
     182             : 
     183           0 :                 r = read_line(f, LONG_LINE_MAX, &line);
     184           0 :                 if (r < 0)
     185           0 :                         return log_warning_errno(r, "Failed to read /proc/sysvipc/msg: %m");
     186           0 :                 if (r == 0)
     187           0 :                         break;
     188             : 
     189           0 :                 if (first) {
     190           0 :                         first = false;
     191           0 :                         continue;
     192             :                 }
     193             : 
     194           0 :                 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
     195             :                            &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
     196           0 :                         continue;
     197             : 
     198           0 :                 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
     199           0 :                         continue;
     200             : 
     201           0 :                 if (!rm)
     202           0 :                         return 1;
     203             : 
     204           0 :                 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
     205             : 
     206             :                         /* Ignore entries that are already deleted */
     207           0 :                         if (IN_SET(errno, EIDRM, EINVAL))
     208           0 :                                 continue;
     209             : 
     210           0 :                         ret = log_warning_errno(errno,
     211             :                                                 "Failed to remove SysV message queue %i: %m",
     212             :                                                 msgid);
     213             :                 } else {
     214           0 :                         log_debug("Removed SysV message queue %i.", msgid);
     215           0 :                         if (ret == 0)
     216           0 :                                 ret = 1;
     217             :                 }
     218             :         }
     219             : 
     220           0 :         return ret;
     221             : }
     222             : 
     223           0 : static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid, bool rm) {
     224             :         struct dirent *de;
     225           0 :         int ret = 0, r;
     226             : 
     227           0 :         assert(dir);
     228             : 
     229           0 :         FOREACH_DIRENT_ALL(de, dir, goto fail) {
     230             :                 struct stat st;
     231             : 
     232           0 :                 if (dot_or_dot_dot(de->d_name))
     233           0 :                         continue;
     234             : 
     235           0 :                 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
     236           0 :                         if (errno == ENOENT)
     237           0 :                                 continue;
     238             : 
     239           0 :                         ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
     240           0 :                         continue;
     241             :                 }
     242             : 
     243           0 :                 if (S_ISDIR(st.st_mode)) {
     244           0 :                         _cleanup_closedir_ DIR *kid;
     245             : 
     246           0 :                         kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
     247           0 :                         if (!kid) {
     248           0 :                                 if (errno != ENOENT)
     249           0 :                                         ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
     250             :                         } else {
     251           0 :                                 r = clean_posix_shm_internal(kid, uid, gid, rm);
     252           0 :                                 if (r < 0)
     253           0 :                                         ret = r;
     254             :                         }
     255             : 
     256           0 :                         if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
     257           0 :                                 continue;
     258             : 
     259           0 :                         if (!rm)
     260           0 :                                 return 1;
     261             : 
     262           0 :                         if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
     263             : 
     264           0 :                                 if (errno == ENOENT)
     265           0 :                                         continue;
     266             : 
     267           0 :                                 ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
     268             :                         } else {
     269           0 :                                 log_debug("Removed POSIX shared memory directory %s", de->d_name);
     270           0 :                                 if (ret == 0)
     271           0 :                                         ret = 1;
     272             :                         }
     273             :                 } else {
     274             : 
     275           0 :                         if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
     276           0 :                                 continue;
     277             : 
     278           0 :                         if (!rm)
     279           0 :                                 return 1;
     280             : 
     281           0 :                         if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
     282             : 
     283           0 :                                 if (errno == ENOENT)
     284           0 :                                         continue;
     285             : 
     286           0 :                                 ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
     287             :                         } else {
     288           0 :                                 log_debug("Removed POSIX shared memory segment %s", de->d_name);
     289           0 :                                 if (ret == 0)
     290           0 :                                         ret = 1;
     291             :                         }
     292             :                 }
     293             :         }
     294             : 
     295           0 :         return ret;
     296             : 
     297           0 : fail:
     298           0 :         return log_warning_errno(errno, "Failed to read /dev/shm: %m");
     299             : }
     300             : 
     301           0 : static int clean_posix_shm(uid_t uid, gid_t gid, bool rm) {
     302           0 :         _cleanup_closedir_ DIR *dir = NULL;
     303             : 
     304           0 :         dir = opendir("/dev/shm");
     305           0 :         if (!dir) {
     306           0 :                 if (errno == ENOENT)
     307           0 :                         return 0;
     308             : 
     309           0 :                 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
     310             :         }
     311             : 
     312           0 :         return clean_posix_shm_internal(dir, uid, gid, rm);
     313             : }
     314             : 
     315           0 : static int clean_posix_mq(uid_t uid, gid_t gid, bool rm) {
     316           0 :         _cleanup_closedir_ DIR *dir = NULL;
     317             :         struct dirent *de;
     318           0 :         int ret = 0;
     319             : 
     320           0 :         dir = opendir("/dev/mqueue");
     321           0 :         if (!dir) {
     322           0 :                 if (errno == ENOENT)
     323           0 :                         return 0;
     324             : 
     325           0 :                 return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
     326             :         }
     327             : 
     328           0 :         FOREACH_DIRENT_ALL(de, dir, goto fail) {
     329             :                 struct stat st;
     330           0 :                 char fn[1+strlen(de->d_name)+1];
     331             : 
     332           0 :                 if (dot_or_dot_dot(de->d_name))
     333           0 :                         continue;
     334             : 
     335           0 :                 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
     336           0 :                         if (errno == ENOENT)
     337           0 :                                 continue;
     338             : 
     339           0 :                         ret = log_warning_errno(errno,
     340             :                                                 "Failed to stat() MQ segment %s: %m",
     341             :                                                 de->d_name);
     342           0 :                         continue;
     343             :                 }
     344             : 
     345           0 :                 if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
     346           0 :                         continue;
     347             : 
     348           0 :                 if (!rm)
     349           0 :                         return 1;
     350             : 
     351           0 :                 fn[0] = '/';
     352           0 :                 strcpy(fn+1, de->d_name);
     353             : 
     354           0 :                 if (mq_unlink(fn) < 0) {
     355           0 :                         if (errno == ENOENT)
     356           0 :                                 continue;
     357             : 
     358           0 :                         ret = log_warning_errno(errno,
     359             :                                                 "Failed to unlink POSIX message queue %s: %m",
     360             :                                                 fn);
     361             :                 } else {
     362           0 :                         log_debug("Removed POSIX message queue %s", fn);
     363           0 :                         if (ret == 0)
     364           0 :                                 ret = 1;
     365             :                 }
     366             :         }
     367             : 
     368           0 :         return ret;
     369             : 
     370           0 : fail:
     371           0 :         return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
     372             : }
     373             : 
     374           0 : int clean_ipc_internal(uid_t uid, gid_t gid, bool rm) {
     375           0 :         int ret = 0, r;
     376             : 
     377             :         /* If 'rm' is true, clean all IPC objects owned by either the specified UID or the specified GID. Return the
     378             :          * last error encountered or == 0 if no matching IPC objects have been found or > 0 if matching IPC objects
     379             :          * have been found and have been removed.
     380             :          *
     381             :          * If 'rm' is false, just search for IPC objects owned by either the specified UID or the specified GID. In
     382             :          * this case we return < 0 on error, > 0 if we found a matching object, == 0 if we didn't.
     383             :          *
     384             :          * As special rule: if UID/GID is specified as root we'll silently not clean up things, and always claim that
     385             :          * there are IPC objects for it. */
     386             : 
     387           0 :         if (uid == 0) {
     388           0 :                 if (!rm)
     389           0 :                         return 1;
     390             : 
     391           0 :                 uid = UID_INVALID;
     392             :         }
     393           0 :         if (gid == 0) {
     394           0 :                 if (!rm)
     395           0 :                         return 1;
     396             : 
     397           0 :                 gid = GID_INVALID;
     398             :         }
     399             : 
     400             :         /* Anything to do? */
     401           0 :         if (!uid_is_valid(uid) && !gid_is_valid(gid))
     402           0 :                 return 0;
     403             : 
     404           0 :         r = clean_sysvipc_shm(uid, gid, rm);
     405           0 :         if (r != 0) {
     406           0 :                 if (!rm)
     407           0 :                         return r;
     408           0 :                 if (ret == 0)
     409           0 :                         ret = r;
     410             :         }
     411             : 
     412           0 :         r = clean_sysvipc_sem(uid, gid, rm);
     413           0 :         if (r != 0) {
     414           0 :                 if (!rm)
     415           0 :                         return r;
     416           0 :                 if (ret == 0)
     417           0 :                         ret = r;
     418             :         }
     419             : 
     420           0 :         r = clean_sysvipc_msg(uid, gid, rm);
     421           0 :         if (r != 0) {
     422           0 :                 if (!rm)
     423           0 :                         return r;
     424           0 :                 if (ret == 0)
     425           0 :                         ret = r;
     426             :         }
     427             : 
     428           0 :         r = clean_posix_shm(uid, gid, rm);
     429           0 :         if (r != 0) {
     430           0 :                 if (!rm)
     431           0 :                         return r;
     432           0 :                 if (ret == 0)
     433           0 :                         ret = r;
     434             :         }
     435             : 
     436           0 :         r = clean_posix_mq(uid, gid, rm);
     437           0 :         if (r != 0) {
     438           0 :                 if (!rm)
     439           0 :                         return r;
     440           0 :                 if (ret == 0)
     441           0 :                         ret = r;
     442             :         }
     443             : 
     444           0 :         return ret;
     445             : }
     446             : 
     447           0 : int clean_ipc_by_uid(uid_t uid) {
     448           0 :         return clean_ipc_internal(uid, GID_INVALID, true);
     449             : }
     450             : 
     451           0 : int clean_ipc_by_gid(gid_t gid) {
     452           0 :         return clean_ipc_internal(UID_INVALID, gid, true);
     453             : }

Generated by: LCOV version 1.14