LCOV - code coverage report
Current view: top level - nspawn - nspawn-setuid.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 137 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 3 0.0 %

          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