LCOV - code coverage report
Current view: top level - shared - clean-ipc.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 0 185 0.0 %
Date: 2015-07-29 18:47:03 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2014 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <sys/ipc.h>
      23             : #include <sys/shm.h>
      24             : #include <sys/sem.h>
      25             : #include <sys/msg.h>
      26             : #include <sys/stat.h>
      27             : #include <fcntl.h>
      28             : #include <dirent.h>
      29             : #include <mqueue.h>
      30             : 
      31             : #include "util.h"
      32             : #include "formats-util.h"
      33             : #include "strv.h"
      34             : #include "clean-ipc.h"
      35             : 
      36           0 : static int clean_sysvipc_shm(uid_t delete_uid) {
      37           0 :         _cleanup_fclose_ FILE *f = NULL;
      38             :         char line[LINE_MAX];
      39           0 :         bool first = true;
      40           0 :         int ret = 0;
      41             : 
      42           0 :         f = fopen("/proc/sysvipc/shm", "re");
      43           0 :         if (!f) {
      44           0 :                 if (errno == ENOENT)
      45           0 :                         return 0;
      46             : 
      47           0 :                 log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
      48           0 :                 return -errno;
      49             :         }
      50             : 
      51           0 :         FOREACH_LINE(line, f, goto fail) {
      52             :                 unsigned n_attached;
      53             :                 pid_t cpid, lpid;
      54             :                 uid_t uid, cuid;
      55             :                 gid_t gid, cgid;
      56             :                 int shmid;
      57             : 
      58           0 :                 if (first) {
      59           0 :                         first = false;
      60           0 :                         continue;
      61             :                 }
      62             : 
      63           0 :                 truncate_nl(line);
      64             : 
      65           0 :                 if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
      66             :                            &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
      67           0 :                         continue;
      68             : 
      69           0 :                 if (n_attached > 0)
      70           0 :                         continue;
      71             : 
      72           0 :                 if (uid != delete_uid)
      73           0 :                         continue;
      74             : 
      75           0 :                 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
      76             : 
      77             :                         /* Ignore entries that are already deleted */
      78           0 :                         if (errno == EIDRM || errno == EINVAL)
      79           0 :                                 continue;
      80             : 
      81           0 :                         log_warning_errno(errno, "Failed to remove SysV shared memory segment %i: %m", shmid);
      82           0 :                         ret = -errno;
      83             :                 }
      84           0 :         }
      85             : 
      86           0 :         return ret;
      87             : 
      88             : fail:
      89           0 :         log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
      90           0 :         return -errno;
      91             : }
      92             : 
      93           0 : static int clean_sysvipc_sem(uid_t delete_uid) {
      94           0 :         _cleanup_fclose_ FILE *f = NULL;
      95             :         char line[LINE_MAX];
      96           0 :         bool first = true;
      97           0 :         int ret = 0;
      98             : 
      99           0 :         f = fopen("/proc/sysvipc/sem", "re");
     100           0 :         if (!f) {
     101           0 :                 if (errno == ENOENT)
     102           0 :                         return 0;
     103             : 
     104           0 :                 log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
     105           0 :                 return -errno;
     106             :         }
     107             : 
     108           0 :         FOREACH_LINE(line, f, goto fail) {
     109             :                 uid_t uid, cuid;
     110             :                 gid_t gid, cgid;
     111             :                 int semid;
     112             : 
     113           0 :                 if (first) {
     114           0 :                         first = false;
     115           0 :                         continue;
     116             :                 }
     117             : 
     118           0 :                 truncate_nl(line);
     119             : 
     120           0 :                 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
     121             :                            &semid, &uid, &gid, &cuid, &cgid) != 5)
     122           0 :                         continue;
     123             : 
     124           0 :                 if (uid != delete_uid)
     125           0 :                         continue;
     126             : 
     127           0 :                 if (semctl(semid, 0, IPC_RMID) < 0) {
     128             : 
     129             :                         /* Ignore entries that are already deleted */
     130           0 :                         if (errno == EIDRM || errno == EINVAL)
     131           0 :                                 continue;
     132             : 
     133           0 :                         log_warning_errno(errno, "Failed to remove SysV semaphores object %i: %m", semid);
     134           0 :                         ret = -errno;
     135             :                 }
     136           0 :         }
     137             : 
     138           0 :         return ret;
     139             : 
     140             : fail:
     141           0 :         log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
     142           0 :         return -errno;
     143             : }
     144             : 
     145           0 : static int clean_sysvipc_msg(uid_t delete_uid) {
     146           0 :         _cleanup_fclose_ FILE *f = NULL;
     147             :         char line[LINE_MAX];
     148           0 :         bool first = true;
     149           0 :         int ret = 0;
     150             : 
     151           0 :         f = fopen("/proc/sysvipc/msg", "re");
     152           0 :         if (!f) {
     153           0 :                 if (errno == ENOENT)
     154           0 :                         return 0;
     155             : 
     156           0 :                 log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
     157           0 :                 return -errno;
     158             :         }
     159             : 
     160           0 :         FOREACH_LINE(line, f, goto fail) {
     161             :                 uid_t uid, cuid;
     162             :                 gid_t gid, cgid;
     163             :                 pid_t cpid, lpid;
     164             :                 int msgid;
     165             : 
     166           0 :                 if (first) {
     167           0 :                         first = false;
     168           0 :                         continue;
     169             :                 }
     170             : 
     171           0 :                 truncate_nl(line);
     172             : 
     173           0 :                 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
     174             :                            &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
     175           0 :                         continue;
     176             : 
     177           0 :                 if (uid != delete_uid)
     178           0 :                         continue;
     179             : 
     180           0 :                 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
     181             : 
     182             :                         /* Ignore entries that are already deleted */
     183           0 :                         if (errno == EIDRM || errno == EINVAL)
     184           0 :                                 continue;
     185             : 
     186           0 :                         log_warning_errno(errno, "Failed to remove SysV message queue %i: %m", msgid);
     187           0 :                         ret = -errno;
     188             :                 }
     189           0 :         }
     190             : 
     191           0 :         return ret;
     192             : 
     193             : fail:
     194           0 :         log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
     195           0 :         return -errno;
     196             : }
     197             : 
     198           0 : static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
     199             :         struct dirent *de;
     200           0 :         int ret = 0, r;
     201             : 
     202           0 :         assert(dir);
     203             : 
     204           0 :         FOREACH_DIRENT(de, dir, goto fail) {
     205             :                 struct stat st;
     206             : 
     207           0 :                 if (STR_IN_SET(de->d_name, "..", "."))
     208           0 :                         continue;
     209             : 
     210           0 :                 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
     211           0 :                         if (errno == ENOENT)
     212           0 :                                 continue;
     213             : 
     214           0 :                         log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
     215           0 :                         ret = -errno;
     216           0 :                         continue;
     217             :                 }
     218             : 
     219           0 :                 if (st.st_uid != uid)
     220           0 :                         continue;
     221             : 
     222           0 :                 if (S_ISDIR(st.st_mode)) {
     223           0 :                         _cleanup_closedir_ DIR *kid;
     224             : 
     225           0 :                         kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
     226           0 :                         if (!kid) {
     227           0 :                                 if (errno != ENOENT) {
     228           0 :                                         log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
     229           0 :                                         ret = -errno;
     230             :                                 }
     231             :                         } else {
     232           0 :                                 r = clean_posix_shm_internal(kid, uid);
     233           0 :                                 if (r < 0)
     234           0 :                                         ret = r;
     235             :                         }
     236             : 
     237           0 :                         if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
     238             : 
     239           0 :                                 if (errno == ENOENT)
     240           0 :                                         continue;
     241             : 
     242           0 :                                 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
     243           0 :                                 ret = -errno;
     244             :                         }
     245             :                 } else {
     246             : 
     247           0 :                         if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
     248             : 
     249           0 :                                 if (errno == ENOENT)
     250           0 :                                         continue;
     251             : 
     252           0 :                                 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
     253           0 :                                 ret = -errno;
     254             :                         }
     255             :                 }
     256           0 :         }
     257             : 
     258           0 :         return ret;
     259             : 
     260             : fail:
     261           0 :         log_warning_errno(errno, "Failed to read /dev/shm: %m");
     262           0 :         return -errno;
     263             : }
     264             : 
     265           0 : static int clean_posix_shm(uid_t uid) {
     266           0 :         _cleanup_closedir_ DIR *dir = NULL;
     267             : 
     268           0 :         dir = opendir("/dev/shm");
     269           0 :         if (!dir) {
     270           0 :                 if (errno == ENOENT)
     271           0 :                         return 0;
     272             : 
     273           0 :                 log_warning_errno(errno, "Failed to open /dev/shm: %m");
     274           0 :                 return -errno;
     275             :         }
     276             : 
     277           0 :         return clean_posix_shm_internal(dir, uid);
     278             : }
     279             : 
     280           0 : static int clean_posix_mq(uid_t uid) {
     281           0 :         _cleanup_closedir_ DIR *dir = NULL;
     282             :         struct dirent *de;
     283           0 :         int ret = 0;
     284             : 
     285           0 :         dir = opendir("/dev/mqueue");
     286           0 :         if (!dir) {
     287           0 :                 if (errno == ENOENT)
     288           0 :                         return 0;
     289             : 
     290           0 :                 log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
     291           0 :                 return -errno;
     292             :         }
     293             : 
     294           0 :         FOREACH_DIRENT(de, dir, goto fail) {
     295             :                 struct stat st;
     296           0 :                 char fn[1+strlen(de->d_name)+1];
     297             : 
     298           0 :                 if (STR_IN_SET(de->d_name, "..", "."))
     299           0 :                         continue;
     300             : 
     301           0 :                 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
     302           0 :                         if (errno == ENOENT)
     303           0 :                                 continue;
     304             : 
     305           0 :                         log_warning_errno(errno, "Failed to stat() MQ segment %s: %m", de->d_name);
     306           0 :                         ret = -errno;
     307           0 :                         continue;
     308             :                 }
     309             : 
     310           0 :                 if (st.st_uid != uid)
     311           0 :                         continue;
     312             : 
     313           0 :                 fn[0] = '/';
     314           0 :                 strcpy(fn+1, de->d_name);
     315             : 
     316           0 :                 if (mq_unlink(fn) < 0) {
     317           0 :                         if (errno == ENOENT)
     318           0 :                                 continue;
     319             : 
     320           0 :                         log_warning_errno(errno, "Failed to unlink POSIX message queue %s: %m", fn);
     321           0 :                         ret = -errno;
     322             :                 }
     323           0 :         }
     324             : 
     325           0 :         return ret;
     326             : 
     327             : fail:
     328           0 :         log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
     329           0 :         return -errno;
     330             : }
     331             : 
     332           0 : int clean_ipc(uid_t uid) {
     333           0 :         int ret = 0, r;
     334             : 
     335             :         /* Refuse to clean IPC of the root and system users */
     336           0 :         if (uid <= SYSTEM_UID_MAX)
     337           0 :                 return 0;
     338             : 
     339           0 :         r = clean_sysvipc_shm(uid);
     340           0 :         if (r < 0)
     341           0 :                 ret = r;
     342             : 
     343           0 :         r = clean_sysvipc_sem(uid);
     344           0 :         if (r < 0)
     345           0 :                 ret = r;
     346             : 
     347           0 :         r = clean_sysvipc_msg(uid);
     348           0 :         if (r < 0)
     349           0 :                 ret = r;
     350             : 
     351           0 :         r = clean_posix_shm(uid);
     352           0 :         if (r < 0)
     353           0 :                 ret = r;
     354             : 
     355           0 :         r = clean_posix_mq(uid);
     356           0 :         if (r < 0)
     357           0 :                 ret = r;
     358             : 
     359           0 :         return ret;
     360             : }

Generated by: LCOV version 1.11