LCOV - code coverage report
Current view: top level - nspawn - nspawn-setuid.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 137 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 3 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 124 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <fcntl.h>
       4                 :            : #include <grp.h>
       5                 :            : #include <sys/types.h>
       6                 :            : #include <unistd.h>
       7                 :            : 
       8                 :            : #include "alloc-util.h"
       9                 :            : #include "def.h"
      10                 :            : #include "errno.h"
      11                 :            : #include "fd-util.h"
      12                 :            : #include "fileio.h"
      13                 :            : #include "mkdir.h"
      14                 :            : #include "nspawn-setuid.h"
      15                 :            : #include "process-util.h"
      16                 :            : #include "rlimit-util.h"
      17                 :            : #include "signal-util.h"
      18                 :            : #include "string-util.h"
      19                 :            : #include "strv.h"
      20                 :            : #include "user-util.h"
      21                 :            : #include "util.h"
      22                 :            : 
      23                 :          0 : static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
      24                 :            :         int pipe_fds[2], r;
      25                 :            :         pid_t pid;
      26                 :            : 
      27         [ #  # ]:          0 :         assert(database);
      28         [ #  # ]:          0 :         assert(key);
      29         [ #  # ]:          0 :         assert(rpid);
      30                 :            : 
      31         [ #  # ]:          0 :         if (pipe2(pipe_fds, O_CLOEXEC) < 0)
      32         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to allocate pipe: %m");
      33                 :            : 
      34                 :          0 :         r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
      35         [ #  # ]:          0 :         if (r < 0) {
      36                 :          0 :                 safe_close_pair(pipe_fds);
      37                 :          0 :                 return r;
      38                 :            :         }
      39         [ #  # ]:          0 :         if (r == 0) {
      40                 :          0 :                 char *empty_env = NULL;
      41                 :            : 
      42                 :          0 :                 safe_close(pipe_fds[0]);
      43                 :            : 
      44         [ #  # ]:          0 :                 if (rearrange_stdio(-1, pipe_fds[1], -1) < 0)
      45                 :          0 :                         _exit(EXIT_FAILURE);
      46                 :            : 
      47                 :          0 :                 (void) close_all_fds(NULL, 0);
      48                 :            : 
      49                 :          0 :                 (void) rlimit_nofile_safe();
      50                 :            : 
      51                 :          0 :                 execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
      52                 :          0 :                 execle("/bin/getent", "getent", database, key, NULL, &empty_env);
      53                 :          0 :                 _exit(EXIT_FAILURE);
      54                 :            :         }
      55                 :            : 
      56                 :          0 :         pipe_fds[1] = safe_close(pipe_fds[1]);
      57                 :            : 
      58                 :          0 :         *rpid = pid;
      59                 :            : 
      60                 :          0 :         return pipe_fds[0];
      61                 :            : }
      62                 :            : 
      63                 :          0 : int change_uid_gid_raw(
      64                 :            :                 uid_t uid,
      65                 :            :                 gid_t gid,
      66                 :            :                 const gid_t *supplementary_gids,
      67                 :            :                 size_t n_supplementary_gids) {
      68                 :            : 
      69         [ #  # ]:          0 :         if (!uid_is_valid(uid))
      70                 :          0 :                 uid = 0;
      71         [ #  # ]:          0 :         if (!gid_is_valid(gid))
      72                 :          0 :                 gid = 0;
      73                 :            : 
      74                 :          0 :         (void) fchown(STDIN_FILENO, uid, gid);
      75                 :          0 :         (void) fchown(STDOUT_FILENO, uid, gid);
      76                 :          0 :         (void) fchown(STDERR_FILENO, uid, gid);
      77                 :            : 
      78         [ #  # ]:          0 :         if (setgroups(n_supplementary_gids, supplementary_gids) < 0)
      79         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to set auxiliary groups: %m");
      80                 :            : 
      81         [ #  # ]:          0 :         if (setresgid(gid, gid, gid) < 0)
      82         [ #  # ]:          0 :                 return log_error_errno(errno, "setresgid() failed: %m");
      83                 :            : 
      84         [ #  # ]:          0 :         if (setresuid(uid, uid, uid) < 0)
      85         [ #  # ]:          0 :                 return log_error_errno(errno, "setresuid() failed: %m");
      86                 :            : 
      87                 :          0 :         return 0;
      88                 :            : }
      89                 :            : 
      90                 :          0 : int change_uid_gid(const char *user, char **_home) {
      91                 :            :         char *x, *u, *g, *h;
      92                 :            :         const char *word, *state;
      93                 :          0 :         _cleanup_free_ gid_t *gids = NULL;
      94                 :          0 :         _cleanup_free_ char *home = NULL, *line = NULL;
      95                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
      96                 :          0 :         _cleanup_close_ int fd = -1;
      97                 :          0 :         unsigned n_gids = 0;
      98                 :          0 :         size_t sz = 0, l;
      99                 :            :         uid_t uid;
     100                 :            :         gid_t gid;
     101                 :            :         pid_t pid;
     102                 :            :         int r;
     103                 :            : 
     104         [ #  # ]:          0 :         assert(_home);
     105                 :            : 
     106   [ #  #  #  # ]:          0 :         if (!user || STR_IN_SET(user, "root", "0")) {
     107                 :            :                 /* Reset everything fully to 0, just in case */
     108                 :            : 
     109                 :          0 :                 r = reset_uid_gid();
     110         [ #  # ]:          0 :                 if (r < 0)
     111         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to become root: %m");
     112                 :            : 
     113                 :          0 :                 *_home = NULL;
     114                 :          0 :                 return 0;
     115                 :            :         }
     116                 :            : 
     117                 :            :         /* First, get user credentials */
     118                 :          0 :         fd = spawn_getent("passwd", user, &pid);
     119         [ #  # ]:          0 :         if (fd < 0)
     120                 :          0 :                 return fd;
     121                 :            : 
     122                 :          0 :         f = fdopen(fd, "r");
     123         [ #  # ]:          0 :         if (!f)
     124                 :          0 :                 return log_oom();
     125                 :          0 :         fd = -1;
     126                 :            : 
     127                 :          0 :         r = read_line(f, LONG_LINE_MAX, &line);
     128         [ #  # ]:          0 :         if (r == 0)
     129         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
     130                 :            :                                        "Failed to resolve user %s.", user);
     131         [ #  # ]:          0 :         if (r < 0)
     132         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to read from getent: %m");
     133                 :            : 
     134                 :          0 :         (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
     135                 :            : 
     136                 :          0 :         x = strchr(line, ':');
     137         [ #  # ]:          0 :         if (!x)
     138         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     139                 :            :                                        "/etc/passwd entry has invalid user field.");
     140                 :            : 
     141                 :          0 :         u = strchr(x+1, ':');
     142         [ #  # ]:          0 :         if (!u)
     143         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     144                 :            :                                        "/etc/passwd entry has invalid password field.");
     145                 :            : 
     146                 :          0 :         u++;
     147                 :          0 :         g = strchr(u, ':');
     148         [ #  # ]:          0 :         if (!g)
     149         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     150                 :            :                                        "/etc/passwd entry has invalid UID field.");
     151                 :            : 
     152                 :          0 :         *g = 0;
     153                 :          0 :         g++;
     154                 :          0 :         x = strchr(g, ':');
     155         [ #  # ]:          0 :         if (!x)
     156         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     157                 :            :                                        "/etc/passwd entry has invalid GID field.");
     158                 :            : 
     159                 :          0 :         *x = 0;
     160                 :          0 :         h = strchr(x+1, ':');
     161         [ #  # ]:          0 :         if (!h)
     162         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     163                 :            :                                        "/etc/passwd entry has invalid GECOS field.");
     164                 :            : 
     165                 :          0 :         h++;
     166                 :          0 :         x = strchr(h, ':');
     167         [ #  # ]:          0 :         if (!x)
     168         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     169                 :            :                                        "/etc/passwd entry has invalid home directory field.");
     170                 :            : 
     171                 :          0 :         *x = 0;
     172                 :            : 
     173                 :          0 :         r = parse_uid(u, &uid);
     174         [ #  # ]:          0 :         if (r < 0)
     175         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     176                 :            :                                        "Failed to parse UID of user.");
     177                 :            : 
     178                 :          0 :         r = parse_gid(g, &gid);
     179         [ #  # ]:          0 :         if (r < 0)
     180         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
     181                 :            :                                        "Failed to parse GID of user.");
     182                 :            : 
     183                 :          0 :         home = strdup(h);
     184         [ #  # ]:          0 :         if (!home)
     185                 :          0 :                 return log_oom();
     186                 :            : 
     187                 :          0 :         f = safe_fclose(f);
     188                 :          0 :         line = mfree(line);
     189                 :            : 
     190                 :            :         /* Second, get group memberships */
     191                 :          0 :         fd = spawn_getent("initgroups", user, &pid);
     192         [ #  # ]:          0 :         if (fd < 0)
     193                 :          0 :                 return fd;
     194                 :            : 
     195                 :          0 :         f = fdopen(fd, "r");
     196         [ #  # ]:          0 :         if (!f)
     197                 :          0 :                 return log_oom();
     198                 :          0 :         fd = -1;
     199                 :            : 
     200                 :          0 :         r = read_line(f, LONG_LINE_MAX, &line);
     201         [ #  # ]:          0 :         if (r == 0)
     202         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
     203                 :            :                                        "Failed to resolve user %s.", user);
     204         [ #  # ]:          0 :         if (r < 0)
     205         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to read from getent: %m");
     206                 :            : 
     207                 :          0 :         (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
     208                 :            : 
     209                 :            :         /* Skip over the username and subsequent separator whitespace */
     210                 :          0 :         x = line;
     211                 :          0 :         x += strcspn(x, WHITESPACE);
     212                 :          0 :         x += strspn(x, WHITESPACE);
     213                 :            : 
     214         [ #  # ]:          0 :         FOREACH_WORD(word, l, x, state) {
     215                 :          0 :                 char c[l+1];
     216                 :            : 
     217                 :          0 :                 memcpy(c, word, l);
     218                 :          0 :                 c[l] = 0;
     219                 :            : 
     220         [ #  # ]:          0 :                 if (!GREEDY_REALLOC(gids, sz, n_gids+1))
     221                 :          0 :                         return log_oom();
     222                 :            : 
     223                 :          0 :                 r = parse_gid(c, &gids[n_gids++]);
     224         [ #  # ]:          0 :                 if (r < 0)
     225         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to parse group data from getent: %m");
     226                 :            :         }
     227                 :            : 
     228                 :          0 :         r = mkdir_parents(home, 0775);
     229         [ #  # ]:          0 :         if (r < 0)
     230         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to make home root directory: %m");
     231                 :            : 
     232                 :          0 :         r = mkdir_safe(home, 0755, uid, gid, 0);
     233   [ #  #  #  #  :          0 :         if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR))
                   #  # ]
     234         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to make home directory: %m");
     235                 :            : 
     236                 :          0 :         r = change_uid_gid_raw(uid, gid, gids, n_gids);
     237         [ #  # ]:          0 :         if (r < 0)
     238                 :          0 :                 return r;
     239                 :            : 
     240         [ #  # ]:          0 :         if (_home)
     241                 :          0 :                 *_home = TAKE_PTR(home);
     242                 :            : 
     243                 :          0 :         return 0;
     244                 :            : }

Generated by: LCOV version 1.14