LCOV - code coverage report
Current view: top level - basic - user-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 230 448 51.3 %
Date: 2019-08-23 13:36:53 Functions: 18 31 58.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 165 436 37.8 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <alloca.h>
       4                 :            : #include <errno.h>
       5                 :            : #include <fcntl.h>
       6                 :            : #include <grp.h>
       7                 :            : #include <pwd.h>
       8                 :            : #include <stddef.h>
       9                 :            : #include <stdint.h>
      10                 :            : #include <stdio.h>
      11                 :            : #include <stdlib.h>
      12                 :            : #include <string.h>
      13                 :            : #include <sys/stat.h>
      14                 :            : #include <unistd.h>
      15                 :            : #include <utmp.h>
      16                 :            : 
      17                 :            : #include "alloc-util.h"
      18                 :            : #include "errno-util.h"
      19                 :            : #include "fd-util.h"
      20                 :            : #include "fileio.h"
      21                 :            : #include "format-util.h"
      22                 :            : #include "macro.h"
      23                 :            : #include "missing.h"
      24                 :            : #include "parse-util.h"
      25                 :            : #include "path-util.h"
      26                 :            : #include "random-util.h"
      27                 :            : #include "string-util.h"
      28                 :            : #include "strv.h"
      29                 :            : #include "user-util.h"
      30                 :            : #include "utf8.h"
      31                 :            : 
      32                 :      20408 : bool uid_is_valid(uid_t uid) {
      33                 :            : 
      34                 :            :         /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
      35                 :            : 
      36                 :            :         /* Some libc APIs use UID_INVALID as special placeholder */
      37         [ +  + ]:      20408 :         if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
      38                 :      17576 :                 return false;
      39                 :            : 
      40                 :            :         /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
      41         [ +  + ]:       2832 :         if (uid == (uid_t) UINT32_C(0xFFFF))
      42                 :         32 :                 return false;
      43                 :            : 
      44                 :       2800 :         return true;
      45                 :            : }
      46                 :            : 
      47                 :       1004 : int parse_uid(const char *s, uid_t *ret) {
      48                 :       1004 :         uint32_t uid = 0;
      49                 :            :         int r;
      50                 :            : 
      51         [ -  + ]:       1004 :         assert(s);
      52                 :            : 
      53                 :            :         assert_cc(sizeof(uid_t) == sizeof(uint32_t));
      54                 :       1004 :         r = safe_atou32(s, &uid);
      55         [ +  + ]:       1004 :         if (r < 0)
      56                 :        148 :                 return r;
      57                 :            : 
      58         [ +  + ]:        856 :         if (!uid_is_valid(uid))
      59                 :         28 :                 return -ENXIO; /* we return ENXIO instead of EINVAL
      60                 :            :                                 * here, to make it easy to distinguish
      61                 :            :                                 * invalid numeric uids from invalid
      62                 :            :                                 * strings. */
      63                 :            : 
      64         [ +  + ]:        828 :         if (ret)
      65                 :        628 :                 *ret = uid;
      66                 :            : 
      67                 :        828 :         return 0;
      68                 :            : }
      69                 :            : 
      70                 :          0 : char* getlogname_malloc(void) {
      71                 :            :         uid_t uid;
      72                 :            :         struct stat st;
      73                 :            : 
      74   [ #  #  #  # ]:          0 :         if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
      75                 :          0 :                 uid = st.st_uid;
      76                 :            :         else
      77                 :          0 :                 uid = getuid();
      78                 :            : 
      79                 :          0 :         return uid_to_name(uid);
      80                 :            : }
      81                 :            : 
      82                 :         20 : char *getusername_malloc(void) {
      83                 :            :         const char *e;
      84                 :            : 
      85                 :         20 :         e = secure_getenv("USER");
      86         [ +  - ]:         20 :         if (e)
      87                 :         20 :                 return strdup(e);
      88                 :            : 
      89                 :          0 :         return uid_to_name(getuid());
      90                 :            : }
      91                 :            : 
      92                 :          0 : static bool is_nologin_shell(const char *shell) {
      93                 :            : 
      94   [ #  #  #  #  :          0 :         return PATH_IN_SET(shell,
                   #  # ]
      95                 :            :                            /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
      96                 :            :                             * message and exits. Different distributions place the binary at different places though,
      97                 :            :                             * hence let's list them all. */
      98                 :            :                            "/bin/nologin",
      99                 :            :                            "/sbin/nologin",
     100                 :            :                            "/usr/bin/nologin",
     101                 :            :                            "/usr/sbin/nologin",
     102                 :            :                            /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
     103                 :            :                             * any message printing. Different distributions place the binary at various places but at
     104                 :            :                             * least not in the 'sbin' directory. */
     105                 :            :                            "/bin/false",
     106                 :            :                            "/usr/bin/false",
     107                 :            :                            "/bin/true",
     108                 :            :                            "/usr/bin/true");
     109                 :            : }
     110                 :            : 
     111                 :         28 : static int synthesize_user_creds(
     112                 :            :                 const char **username,
     113                 :            :                 uid_t *uid, gid_t *gid,
     114                 :            :                 const char **home,
     115                 :            :                 const char **shell,
     116                 :            :                 UserCredsFlags flags) {
     117                 :            : 
     118                 :            :         /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
     119                 :            :          * their user record data. */
     120                 :            : 
     121         [ +  + ]:         28 :         if (STR_IN_SET(*username, "root", "0")) {
     122                 :         12 :                 *username = "root";
     123                 :            : 
     124         [ +  - ]:         12 :                 if (uid)
     125                 :         12 :                         *uid = 0;
     126         [ +  + ]:         12 :                 if (gid)
     127                 :          8 :                         *gid = 0;
     128                 :            : 
     129         [ +  + ]:         12 :                 if (home)
     130                 :          8 :                         *home = "/root";
     131                 :            : 
     132         [ +  + ]:         12 :                 if (shell)
     133                 :          8 :                         *shell = "/bin/sh";
     134                 :            : 
     135                 :         12 :                 return 0;
     136                 :            :         }
     137                 :            : 
     138         [ -  + ]:         16 :         if (synthesize_nobody() &&
     139         [ #  # ]:          0 :             STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
     140                 :          0 :                 *username = NOBODY_USER_NAME;
     141                 :            : 
     142         [ #  # ]:          0 :                 if (uid)
     143                 :          0 :                         *uid = UID_NOBODY;
     144         [ #  # ]:          0 :                 if (gid)
     145                 :          0 :                         *gid = GID_NOBODY;
     146                 :            : 
     147         [ #  # ]:          0 :                 if (home)
     148         [ #  # ]:          0 :                         *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
     149                 :            : 
     150         [ #  # ]:          0 :                 if (shell)
     151         [ #  # ]:          0 :                         *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
     152                 :            : 
     153                 :          0 :                 return 0;
     154                 :            :         }
     155                 :            : 
     156                 :         16 :         return -ENOMEDIUM;
     157                 :            : }
     158                 :            : 
     159                 :         28 : int get_user_creds(
     160                 :            :                 const char **username,
     161                 :            :                 uid_t *uid, gid_t *gid,
     162                 :            :                 const char **home,
     163                 :            :                 const char **shell,
     164                 :            :                 UserCredsFlags flags) {
     165                 :            : 
     166                 :         28 :         uid_t u = UID_INVALID;
     167                 :            :         struct passwd *p;
     168                 :            :         int r;
     169                 :            : 
     170         [ -  + ]:         28 :         assert(username);
     171         [ -  + ]:         28 :         assert(*username);
     172                 :            : 
     173   [ -  +  #  # ]:         28 :         if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
     174         [ #  # ]:          0 :             (!home && !shell)) {
     175                 :            : 
     176                 :            :                 /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
     177                 :            :                  * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
     178                 :            :                  * user database will override the synthetic records instead — except if the user is only interested in
     179                 :            :                  * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
     180                 :            :                  * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
     181                 :            :                  * Simply because there are valid usecase where the user might change the home directory or the shell
     182                 :            :                  * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
     183                 :            :                  * support. */
     184                 :            : 
     185                 :         28 :                 r = synthesize_user_creds(username, uid, gid, home, shell, flags);
     186         [ +  + ]:         28 :                 if (r >= 0)
     187                 :         12 :                         return 0;
     188         [ -  + ]:         16 :                 if (r != -ENOMEDIUM) /* not a username we can synthesize */
     189                 :          0 :                         return r;
     190                 :            :         }
     191                 :            : 
     192         [ +  + ]:         16 :         if (parse_uid(*username, &u) >= 0) {
     193                 :          4 :                 errno = 0;
     194                 :          4 :                 p = getpwuid(u);
     195                 :            : 
     196                 :            :                 /* If there are multiple users with the same id, make sure to leave $USER to the configured value
     197                 :            :                  * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
     198                 :            :                  * then let's pick the real username from /etc/passwd. */
     199         [ +  - ]:          4 :                 if (p)
     200                 :          4 :                         *username = p->pw_name;
     201   [ #  #  #  #  :          0 :                 else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {
             #  #  #  # ]
     202                 :            : 
     203                 :            :                         /* If the specified user is a numeric UID and it isn't in the user database, and the caller
     204                 :            :                          * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that
     205                 :            :                          * and don't complain. */
     206                 :            : 
     207         [ #  # ]:          0 :                         if (uid)
     208                 :          0 :                                 *uid = u;
     209                 :            : 
     210                 :          0 :                         return 0;
     211                 :            :                 }
     212                 :            :         } else {
     213                 :         12 :                 errno = 0;
     214                 :         12 :                 p = getpwnam(*username);
     215                 :            :         }
     216         [ +  + ]:         16 :         if (!p) {
     217                 :          8 :                 r = errno_or_else(ESRCH);
     218                 :            : 
     219                 :            :                 /* If the user requested that we only synthesize as fallback, do so now */
     220         [ -  + ]:          8 :                 if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
     221         [ #  # ]:          0 :                         if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
     222                 :          0 :                                 return 0;
     223                 :            :                 }
     224                 :            : 
     225                 :          8 :                 return r;
     226                 :            :         }
     227                 :            : 
     228         [ +  - ]:          8 :         if (uid) {
     229         [ -  + ]:          8 :                 if (!uid_is_valid(p->pw_uid))
     230                 :          0 :                         return -EBADMSG;
     231                 :            : 
     232                 :          8 :                 *uid = p->pw_uid;
     233                 :            :         }
     234                 :            : 
     235         [ +  - ]:          8 :         if (gid) {
     236         [ -  + ]:          8 :                 if (!gid_is_valid(p->pw_gid))
     237                 :          0 :                         return -EBADMSG;
     238                 :            : 
     239                 :          8 :                 *gid = p->pw_gid;
     240                 :            :         }
     241                 :            : 
     242         [ +  - ]:          8 :         if (home) {
     243   [ -  +  #  # ]:          8 :                 if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
     244                 :          0 :                     (empty_or_root(p->pw_dir) ||
     245         [ #  # ]:          0 :                      !path_is_valid(p->pw_dir) ||
     246         [ #  # ]:          0 :                      !path_is_absolute(p->pw_dir)))
     247                 :          0 :                     *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
     248                 :            :                 else
     249                 :          8 :                         *home = p->pw_dir;
     250                 :            :         }
     251                 :            : 
     252         [ +  - ]:          8 :         if (shell) {
     253   [ -  +  #  # ]:          8 :                 if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
     254                 :          0 :                     (isempty(p->pw_shell) ||
     255         [ #  # ]:          0 :                      !path_is_valid(p->pw_dir) ||
     256   [ #  #  #  # ]:          0 :                      !path_is_absolute(p->pw_shell) ||
     257                 :          0 :                      is_nologin_shell(p->pw_shell)))
     258                 :          0 :                         *shell = NULL;
     259                 :            :                 else
     260                 :          8 :                         *shell = p->pw_shell;
     261                 :            :         }
     262                 :            : 
     263                 :          8 :         return 0;
     264                 :            : }
     265                 :            : 
     266                 :         60 : int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
     267                 :            :         struct group *g;
     268                 :            :         gid_t id;
     269                 :            : 
     270         [ -  + ]:         60 :         assert(groupname);
     271                 :            : 
     272                 :            :         /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
     273                 :            : 
     274         [ +  + ]:         60 :         if (STR_IN_SET(*groupname, "root", "0")) {
     275                 :         12 :                 *groupname = "root";
     276                 :            : 
     277         [ +  - ]:         12 :                 if (gid)
     278                 :         12 :                         *gid = 0;
     279                 :            : 
     280                 :         12 :                 return 0;
     281                 :            :         }
     282                 :            : 
     283         [ -  + ]:         48 :         if (synthesize_nobody() &&
     284         [ #  # ]:          0 :             STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
     285                 :          0 :                 *groupname = NOBODY_GROUP_NAME;
     286                 :            : 
     287         [ #  # ]:          0 :                 if (gid)
     288                 :          0 :                         *gid = GID_NOBODY;
     289                 :            : 
     290                 :          0 :                 return 0;
     291                 :            :         }
     292                 :            : 
     293         [ +  + ]:         48 :         if (parse_gid(*groupname, &id) >= 0) {
     294                 :          4 :                 errno = 0;
     295                 :          4 :                 g = getgrgid(id);
     296                 :            : 
     297         [ +  - ]:          4 :                 if (g)
     298                 :          4 :                         *groupname = g->gr_name;
     299         [ #  # ]:          0 :                 else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
     300         [ #  # ]:          0 :                         if (gid)
     301                 :          0 :                                 *gid = id;
     302                 :            : 
     303                 :          0 :                         return 0;
     304                 :            :                 }
     305                 :            :         } else {
     306                 :         44 :                 errno = 0;
     307                 :         44 :                 g = getgrnam(*groupname);
     308                 :            :         }
     309                 :            : 
     310         [ +  + ]:         48 :         if (!g)
     311                 :          4 :                 return errno_or_else(ESRCH);
     312                 :            : 
     313         [ +  - ]:         44 :         if (gid) {
     314         [ -  + ]:         44 :                 if (!gid_is_valid(g->gr_gid))
     315                 :          0 :                         return -EBADMSG;
     316                 :            : 
     317                 :         44 :                 *gid = g->gr_gid;
     318                 :            :         }
     319                 :            : 
     320                 :         44 :         return 0;
     321                 :            : }
     322                 :            : 
     323                 :         56 : char* uid_to_name(uid_t uid) {
     324                 :            :         char *ret;
     325                 :            :         int r;
     326                 :            : 
     327                 :            :         /* Shortcut things to avoid NSS lookups */
     328         [ +  + ]:         56 :         if (uid == 0)
     329                 :          4 :                 return strdup("root");
     330   [ -  +  #  # ]:         52 :         if (synthesize_nobody() &&
     331                 :            :             uid == UID_NOBODY)
     332                 :          0 :                 return strdup(NOBODY_USER_NAME);
     333                 :            : 
     334         [ +  + ]:         52 :         if (uid_is_valid(uid)) {
     335                 :            :                 long bufsize;
     336                 :            : 
     337                 :         44 :                 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
     338         [ -  + ]:         44 :                 if (bufsize <= 0)
     339                 :          0 :                         bufsize = 4096;
     340                 :            : 
     341                 :          0 :                 for (;;) {
     342                 :         44 :                         struct passwd pwbuf, *pw = NULL;
     343      [ -  +  - ]:         44 :                         _cleanup_free_ char *buf = NULL;
     344                 :            : 
     345                 :         44 :                         buf = malloc(bufsize);
     346         [ -  + ]:         44 :                         if (!buf)
     347                 :          0 :                                 return NULL;
     348                 :            : 
     349                 :         44 :                         r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
     350   [ +  -  +  - ]:         44 :                         if (r == 0 && pw)
     351                 :         44 :                                 return strdup(pw->pw_name);
     352         [ #  # ]:          0 :                         if (r != ERANGE)
     353                 :          0 :                                 break;
     354                 :            : 
     355         [ #  # ]:          0 :                         if (bufsize > LONG_MAX/2) /* overflow check */
     356                 :          0 :                                 return NULL;
     357                 :            : 
     358                 :          0 :                         bufsize *= 2;
     359                 :            :                 }
     360                 :            :         }
     361                 :            : 
     362         [ -  + ]:          8 :         if (asprintf(&ret, UID_FMT, uid) < 0)
     363                 :          0 :                 return NULL;
     364                 :            : 
     365                 :          8 :         return ret;
     366                 :            : }
     367                 :            : 
     368                 :         96 : char* gid_to_name(gid_t gid) {
     369                 :            :         char *ret;
     370                 :            :         int r;
     371                 :            : 
     372         [ +  + ]:         96 :         if (gid == 0)
     373                 :          4 :                 return strdup("root");
     374   [ -  +  #  # ]:         92 :         if (synthesize_nobody() &&
     375                 :            :             gid == GID_NOBODY)
     376                 :          0 :                 return strdup(NOBODY_GROUP_NAME);
     377                 :            : 
     378         [ +  + ]:         92 :         if (gid_is_valid(gid)) {
     379                 :            :                 long bufsize;
     380                 :            : 
     381                 :         84 :                 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
     382         [ -  + ]:         84 :                 if (bufsize <= 0)
     383                 :          0 :                         bufsize = 4096;
     384                 :            : 
     385                 :          0 :                 for (;;) {
     386                 :         84 :                         struct group grbuf, *gr = NULL;
     387      [ -  +  - ]:         84 :                         _cleanup_free_ char *buf = NULL;
     388                 :            : 
     389                 :         84 :                         buf = malloc(bufsize);
     390         [ -  + ]:         84 :                         if (!buf)
     391                 :          0 :                                 return NULL;
     392                 :            : 
     393                 :         84 :                         r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
     394   [ +  -  +  - ]:         84 :                         if (r == 0 && gr)
     395                 :         84 :                                 return strdup(gr->gr_name);
     396         [ #  # ]:          0 :                         if (r != ERANGE)
     397                 :          0 :                                 break;
     398                 :            : 
     399         [ #  # ]:          0 :                         if (bufsize > LONG_MAX/2) /* overflow check */
     400                 :          0 :                                 return NULL;
     401                 :            : 
     402                 :          0 :                         bufsize *= 2;
     403                 :            :                 }
     404                 :            :         }
     405                 :            : 
     406         [ -  + ]:          8 :         if (asprintf(&ret, GID_FMT, gid) < 0)
     407                 :          0 :                 return NULL;
     408                 :            : 
     409                 :          8 :         return ret;
     410                 :            : }
     411                 :            : 
     412                 :         84 : int in_gid(gid_t gid) {
     413                 :            :         long ngroups_max;
     414                 :            :         gid_t *gids;
     415                 :            :         int r, i;
     416                 :            : 
     417         [ +  + ]:         84 :         if (getgid() == gid)
     418                 :         12 :                 return 1;
     419                 :            : 
     420         [ -  + ]:         72 :         if (getegid() == gid)
     421                 :          0 :                 return 1;
     422                 :            : 
     423         [ -  + ]:         72 :         if (!gid_is_valid(gid))
     424                 :          0 :                 return -EINVAL;
     425                 :            : 
     426                 :         72 :         ngroups_max = sysconf(_SC_NGROUPS_MAX);
     427         [ -  + ]:         72 :         assert(ngroups_max > 0);
     428                 :            : 
     429   [ -  +  -  + ]:         72 :         gids = newa(gid_t, ngroups_max);
     430                 :            : 
     431                 :         72 :         r = getgroups(ngroups_max, gids);
     432         [ -  + ]:         72 :         if (r < 0)
     433                 :          0 :                 return -errno;
     434                 :            : 
     435         [ +  + ]:        384 :         for (i = 0; i < r; i++)
     436         [ +  + ]:        376 :                 if (gids[i] == gid)
     437                 :         64 :                         return 1;
     438                 :            : 
     439                 :          8 :         return 0;
     440                 :            : }
     441                 :            : 
     442                 :         44 : int in_group(const char *name) {
     443                 :            :         int r;
     444                 :            :         gid_t gid;
     445                 :            : 
     446                 :         44 :         r = get_group_creds(&name, &gid, 0);
     447         [ +  + ]:         44 :         if (r < 0)
     448                 :          4 :                 return r;
     449                 :            : 
     450                 :         40 :         return in_gid(gid);
     451                 :            : }
     452                 :            : 
     453                 :       1764 : int get_home_dir(char **_h) {
     454                 :            :         struct passwd *p;
     455                 :            :         const char *e;
     456                 :            :         char *h;
     457                 :            :         uid_t u;
     458                 :            : 
     459         [ -  + ]:       1764 :         assert(_h);
     460                 :            : 
     461                 :            :         /* Take the user specified one */
     462                 :       1764 :         e = secure_getenv("HOME");
     463   [ +  +  +  -  :       1764 :         if (e && path_is_valid(e) && path_is_absolute(e)) {
                   +  - ]
     464                 :       1756 :                 h = strdup(e);
     465         [ -  + ]:       1756 :                 if (!h)
     466                 :          0 :                         return -ENOMEM;
     467                 :            : 
     468                 :       1756 :                 *_h = path_simplify(h, true);
     469                 :       1756 :                 return 0;
     470                 :            :         }
     471                 :            : 
     472                 :            :         /* Hardcode home directory for root and nobody to avoid NSS */
     473                 :          8 :         u = getuid();
     474         [ -  + ]:          8 :         if (u == 0) {
     475                 :          0 :                 h = strdup("/root");
     476         [ #  # ]:          0 :                 if (!h)
     477                 :          0 :                         return -ENOMEM;
     478                 :            : 
     479                 :          0 :                 *_h = h;
     480                 :          0 :                 return 0;
     481                 :            :         }
     482   [ -  +  #  # ]:          8 :         if (synthesize_nobody() &&
     483                 :            :             u == UID_NOBODY) {
     484                 :          0 :                 h = strdup("/");
     485         [ #  # ]:          0 :                 if (!h)
     486                 :          0 :                         return -ENOMEM;
     487                 :            : 
     488                 :          0 :                 *_h = h;
     489                 :          0 :                 return 0;
     490                 :            :         }
     491                 :            : 
     492                 :            :         /* Check the database... */
     493                 :          8 :         errno = 0;
     494                 :          8 :         p = getpwuid(u);
     495         [ -  + ]:          8 :         if (!p)
     496                 :          0 :                 return errno_or_else(ESRCH);
     497                 :            : 
     498         [ +  - ]:          8 :         if (!path_is_valid(p->pw_dir) ||
     499         [ -  + ]:          8 :             !path_is_absolute(p->pw_dir))
     500                 :          0 :                 return -EINVAL;
     501                 :            : 
     502                 :          8 :         h = strdup(p->pw_dir);
     503         [ -  + ]:          8 :         if (!h)
     504                 :          0 :                 return -ENOMEM;
     505                 :            : 
     506                 :          8 :         *_h = path_simplify(h, true);
     507                 :          8 :         return 0;
     508                 :            : }
     509                 :            : 
     510                 :          4 : int get_shell(char **_s) {
     511                 :            :         struct passwd *p;
     512                 :            :         const char *e;
     513                 :            :         char *s;
     514                 :            :         uid_t u;
     515                 :            : 
     516         [ -  + ]:          4 :         assert(_s);
     517                 :            : 
     518                 :            :         /* Take the user specified one */
     519                 :          4 :         e = secure_getenv("SHELL");
     520   [ +  -  +  -  :          4 :         if (e && path_is_valid(e) && path_is_absolute(e)) {
                   +  - ]
     521                 :          4 :                 s = strdup(e);
     522         [ -  + ]:          4 :                 if (!s)
     523                 :          0 :                         return -ENOMEM;
     524                 :            : 
     525                 :          4 :                 *_s = path_simplify(s, true);
     526                 :          4 :                 return 0;
     527                 :            :         }
     528                 :            : 
     529                 :            :         /* Hardcode shell for root and nobody to avoid NSS */
     530                 :          0 :         u = getuid();
     531         [ #  # ]:          0 :         if (u == 0) {
     532                 :          0 :                 s = strdup("/bin/sh");
     533         [ #  # ]:          0 :                 if (!s)
     534                 :          0 :                         return -ENOMEM;
     535                 :            : 
     536                 :          0 :                 *_s = s;
     537                 :          0 :                 return 0;
     538                 :            :         }
     539   [ #  #  #  # ]:          0 :         if (synthesize_nobody() &&
     540                 :            :             u == UID_NOBODY) {
     541                 :          0 :                 s = strdup(NOLOGIN);
     542         [ #  # ]:          0 :                 if (!s)
     543                 :          0 :                         return -ENOMEM;
     544                 :            : 
     545                 :          0 :                 *_s = s;
     546                 :          0 :                 return 0;
     547                 :            :         }
     548                 :            : 
     549                 :            :         /* Check the database... */
     550                 :          0 :         errno = 0;
     551                 :          0 :         p = getpwuid(u);
     552         [ #  # ]:          0 :         if (!p)
     553                 :          0 :                 return errno_or_else(ESRCH);
     554                 :            : 
     555         [ #  # ]:          0 :         if (!path_is_valid(p->pw_shell) ||
     556         [ #  # ]:          0 :             !path_is_absolute(p->pw_shell))
     557                 :          0 :                 return -EINVAL;
     558                 :            : 
     559                 :          0 :         s = strdup(p->pw_shell);
     560         [ #  # ]:          0 :         if (!s)
     561                 :          0 :                 return -ENOMEM;
     562                 :            : 
     563                 :          0 :         *_s = path_simplify(s, true);
     564                 :          0 :         return 0;
     565                 :            : }
     566                 :            : 
     567                 :          0 : int reset_uid_gid(void) {
     568                 :            :         int r;
     569                 :            : 
     570                 :          0 :         r = maybe_setgroups(0, NULL);
     571         [ #  # ]:          0 :         if (r < 0)
     572                 :          0 :                 return r;
     573                 :            : 
     574         [ #  # ]:          0 :         if (setresgid(0, 0, 0) < 0)
     575                 :          0 :                 return -errno;
     576                 :            : 
     577         [ #  # ]:          0 :         if (setresuid(0, 0, 0) < 0)
     578                 :          0 :                 return -errno;
     579                 :            : 
     580                 :          0 :         return 0;
     581                 :            : }
     582                 :            : 
     583                 :          0 : int take_etc_passwd_lock(const char *root) {
     584                 :            : 
     585                 :          0 :         struct flock flock = {
     586                 :            :                 .l_type = F_WRLCK,
     587                 :            :                 .l_whence = SEEK_SET,
     588                 :            :                 .l_start = 0,
     589                 :            :                 .l_len = 0,
     590                 :            :         };
     591                 :            : 
     592                 :            :         const char *path;
     593                 :            :         int fd, r;
     594                 :            : 
     595                 :            :         /* This is roughly the same as lckpwdf(), but not as awful. We
     596                 :            :          * don't want to use alarm() and signals, hence we implement
     597                 :            :          * our own trivial version of this.
     598                 :            :          *
     599                 :            :          * Note that shadow-utils also takes per-database locks in
     600                 :            :          * addition to lckpwdf(). However, we don't given that they
     601                 :            :          * are redundant as they invoke lckpwdf() first and keep
     602                 :            :          * it during everything they do. The per-database locks are
     603                 :            :          * awfully racy, and thus we just won't do them. */
     604                 :            : 
     605         [ #  # ]:          0 :         if (root)
     606   [ #  #  #  #  :          0 :                 path = prefix_roota(root, ETC_PASSWD_LOCK_PATH);
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     607                 :            :         else
     608                 :          0 :                 path = ETC_PASSWD_LOCK_PATH;
     609                 :            : 
     610                 :          0 :         fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
     611         [ #  # ]:          0 :         if (fd < 0)
     612         [ #  # ]:          0 :                 return log_debug_errno(errno, "Cannot open %s: %m", path);
     613                 :            : 
     614                 :          0 :         r = fcntl(fd, F_SETLKW, &flock);
     615         [ #  # ]:          0 :         if (r < 0) {
     616                 :          0 :                 safe_close(fd);
     617         [ #  # ]:          0 :                 return log_debug_errno(errno, "Locking %s failed: %m", path);
     618                 :            :         }
     619                 :            : 
     620                 :          0 :         return fd;
     621                 :            : }
     622                 :            : 
     623                 :        172 : bool valid_user_group_name(const char *u) {
     624                 :            :         const char *i;
     625                 :            :         long sz;
     626                 :            : 
     627                 :            :         /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
     628                 :            :          * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
     629                 :            :          *
     630                 :            :          * - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator)
     631                 :            :          * - We require that names fit into the appropriate utmp field
     632                 :            :          * - We don't allow empty user names
     633                 :            :          *
     634                 :            :          * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
     635                 :            :          */
     636                 :            : 
     637         [ +  + ]:        172 :         if (isempty(u))
     638                 :          8 :                 return false;
     639                 :            : 
     640   [ +  +  -  + ]:        164 :         if (!(u[0] >= 'a' && u[0] <= 'z') &&
     641   [ +  +  +  + ]:         92 :             !(u[0] >= 'A' && u[0] <= 'Z') &&
     642         [ +  + ]:         76 :             u[0] != '_')
     643                 :         68 :                 return false;
     644                 :            : 
     645         [ +  + ]:        456 :         for (i = u+1; *i; i++) {
     646   [ +  +  -  + ]:        392 :                 if (!(*i >= 'a' && *i <= 'z') &&
     647   [ +  +  -  + ]:        192 :                     !(*i >= 'A' && *i <= 'Z') &&
     648   [ +  +  +  + ]:         64 :                     !(*i >= '0' && *i <= '9') &&
     649   [ +  +  +  + ]:         48 :                     !IN_SET(*i, '_', '-'))
     650                 :         32 :                         return false;
     651                 :            :         }
     652                 :            : 
     653                 :         64 :         sz = sysconf(_SC_LOGIN_NAME_MAX);
     654         [ -  + ]:         64 :         assert_se(sz > 0);
     655                 :            : 
     656         [ -  + ]:         64 :         if ((size_t) (i-u) > (size_t) sz)
     657                 :          0 :                 return false;
     658                 :            : 
     659         [ -  + ]:         64 :         if ((size_t) (i-u) > UT_NAMESIZE - 1)
     660                 :          0 :                 return false;
     661                 :            : 
     662                 :         64 :         return true;
     663                 :            : }
     664                 :            : 
     665                 :        100 : bool valid_user_group_name_or_id(const char *u) {
     666                 :            : 
     667                 :            :         /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
     668                 :            :          * range, and not the invalid user ids. */
     669                 :            : 
     670         [ +  + ]:        100 :         if (isempty(u))
     671                 :          8 :                 return false;
     672                 :            : 
     673         [ +  + ]:         92 :         if (valid_user_group_name(u))
     674                 :         32 :                 return true;
     675                 :            : 
     676                 :         60 :         return parse_uid(u, NULL) >= 0;
     677                 :            : }
     678                 :            : 
     679                 :         24 : bool valid_gecos(const char *d) {
     680                 :            : 
     681         [ +  + ]:         24 :         if (!d)
     682                 :          4 :                 return false;
     683                 :            : 
     684         [ -  + ]:         20 :         if (!utf8_is_valid(d))
     685                 :          0 :                 return false;
     686                 :            : 
     687         [ +  + ]:         20 :         if (string_has_cc(d, NULL))
     688                 :          4 :                 return false;
     689                 :            : 
     690                 :            :         /* Colons are used as field separators, and hence not OK */
     691         [ +  + ]:         16 :         if (strchr(d, ':'))
     692                 :          4 :                 return false;
     693                 :            : 
     694                 :         12 :         return true;
     695                 :            : }
     696                 :            : 
     697                 :         48 : bool valid_home(const char *p) {
     698                 :            :         /* Note that this function is also called by valid_shell(), any
     699                 :            :          * changes must account for that. */
     700                 :            : 
     701         [ +  + ]:         48 :         if (isempty(p))
     702                 :          8 :                 return false;
     703                 :            : 
     704         [ -  + ]:         40 :         if (!utf8_is_valid(p))
     705                 :          0 :                 return false;
     706                 :            : 
     707         [ +  + ]:         40 :         if (string_has_cc(p, NULL))
     708                 :          4 :                 return false;
     709                 :            : 
     710         [ +  + ]:         36 :         if (!path_is_absolute(p))
     711                 :         12 :                 return false;
     712                 :            : 
     713         [ +  + ]:         24 :         if (!path_is_normalized(p))
     714                 :          8 :                 return false;
     715                 :            : 
     716                 :            :         /* Colons are used as field separators, and hence not OK */
     717         [ +  + ]:         16 :         if (strchr(p, ':'))
     718                 :          4 :                 return false;
     719                 :            : 
     720                 :         12 :         return true;
     721                 :            : }
     722                 :            : 
     723                 :          0 : int maybe_setgroups(size_t size, const gid_t *list) {
     724                 :            :         int r;
     725                 :            : 
     726                 :            :         /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
     727         [ #  # ]:          0 :         if (size == 0) { /* Dropping all aux groups? */
     728         [ #  # ]:          0 :                 _cleanup_free_ char *setgroups_content = NULL;
     729                 :            :                 bool can_setgroups;
     730                 :            : 
     731                 :          0 :                 r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
     732         [ #  # ]:          0 :                 if (r == -ENOENT)
     733                 :            :                         /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
     734                 :          0 :                         can_setgroups = true;
     735         [ #  # ]:          0 :                 else if (r < 0)
     736                 :          0 :                         return r;
     737                 :            :                 else
     738                 :          0 :                         can_setgroups = streq(setgroups_content, "allow");
     739                 :            : 
     740         [ #  # ]:          0 :                 if (!can_setgroups) {
     741         [ #  # ]:          0 :                         log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
     742                 :          0 :                         return 0;
     743                 :            :                 }
     744                 :            :         }
     745                 :            : 
     746         [ #  # ]:          0 :         if (setgroups(size, list) < 0)
     747                 :          0 :                 return -errno;
     748                 :            : 
     749                 :          0 :         return 0;
     750                 :            : }
     751                 :            : 
     752                 :        284 : bool synthesize_nobody(void) {
     753                 :            :         /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
     754                 :            :          * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
     755                 :            :          * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
     756                 :            :          *
     757                 :            :          * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
     758                 :            :          * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
     759                 :            :          * shouldn't matter as each initialization should come to the same result. */
     760                 :            :         static int cache = -1;
     761                 :            : 
     762         [ +  + ]:        284 :         if (cache < 0)
     763                 :         36 :                 cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
     764                 :            : 
     765                 :        284 :         return cache;
     766                 :            : }
     767                 :            : 
     768                 :          0 : int putpwent_sane(const struct passwd *pw, FILE *stream) {
     769         [ #  # ]:          0 :         assert(pw);
     770         [ #  # ]:          0 :         assert(stream);
     771                 :            : 
     772                 :          0 :         errno = 0;
     773         [ #  # ]:          0 :         if (putpwent(pw, stream) != 0)
     774                 :          0 :                 return errno_or_else(EIO);
     775                 :            : 
     776                 :          0 :         return 0;
     777                 :            : }
     778                 :            : 
     779                 :          0 : int putspent_sane(const struct spwd *sp, FILE *stream) {
     780         [ #  # ]:          0 :         assert(sp);
     781         [ #  # ]:          0 :         assert(stream);
     782                 :            : 
     783                 :          0 :         errno = 0;
     784         [ #  # ]:          0 :         if (putspent(sp, stream) != 0)
     785                 :          0 :                 return errno_or_else(EIO);
     786                 :            : 
     787                 :          0 :         return 0;
     788                 :            : }
     789                 :            : 
     790                 :          0 : int putgrent_sane(const struct group *gr, FILE *stream) {
     791         [ #  # ]:          0 :         assert(gr);
     792         [ #  # ]:          0 :         assert(stream);
     793                 :            : 
     794                 :          0 :         errno = 0;
     795         [ #  # ]:          0 :         if (putgrent(gr, stream) != 0)
     796                 :          0 :                 return errno_or_else(EIO);
     797                 :            : 
     798                 :          0 :         return 0;
     799                 :            : }
     800                 :            : 
     801                 :            : #if ENABLE_GSHADOW
     802                 :          0 : int putsgent_sane(const struct sgrp *sg, FILE *stream) {
     803         [ #  # ]:          0 :         assert(sg);
     804         [ #  # ]:          0 :         assert(stream);
     805                 :            : 
     806                 :          0 :         errno = 0;
     807         [ #  # ]:          0 :         if (putsgent(sg, stream) != 0)
     808                 :          0 :                 return errno_or_else(EIO);
     809                 :            : 
     810                 :          0 :         return 0;
     811                 :            : }
     812                 :            : #endif
     813                 :            : 
     814                 :          0 : int fgetpwent_sane(FILE *stream, struct passwd **pw) {
     815                 :            :         struct passwd *p;
     816                 :            : 
     817         [ #  # ]:          0 :         assert(pw);
     818         [ #  # ]:          0 :         assert(stream);
     819                 :            : 
     820                 :          0 :         errno = 0;
     821                 :          0 :         p = fgetpwent(stream);
     822   [ #  #  #  # ]:          0 :         if (!p && errno != ENOENT)
     823                 :          0 :                 return errno_or_else(EIO);
     824                 :            : 
     825                 :          0 :         *pw = p;
     826                 :          0 :         return !!p;
     827                 :            : }
     828                 :            : 
     829                 :          0 : int fgetspent_sane(FILE *stream, struct spwd **sp) {
     830                 :            :         struct spwd *s;
     831                 :            : 
     832         [ #  # ]:          0 :         assert(sp);
     833         [ #  # ]:          0 :         assert(stream);
     834                 :            : 
     835                 :          0 :         errno = 0;
     836                 :          0 :         s = fgetspent(stream);
     837   [ #  #  #  # ]:          0 :         if (!s && errno != ENOENT)
     838                 :          0 :                 return errno_or_else(EIO);
     839                 :            : 
     840                 :          0 :         *sp = s;
     841                 :          0 :         return !!s;
     842                 :            : }
     843                 :            : 
     844                 :          0 : int fgetgrent_sane(FILE *stream, struct group **gr) {
     845                 :            :         struct group *g;
     846                 :            : 
     847         [ #  # ]:          0 :         assert(gr);
     848         [ #  # ]:          0 :         assert(stream);
     849                 :            : 
     850                 :          0 :         errno = 0;
     851                 :          0 :         g = fgetgrent(stream);
     852   [ #  #  #  # ]:          0 :         if (!g && errno != ENOENT)
     853                 :          0 :                 return errno_or_else(EIO);
     854                 :            : 
     855                 :          0 :         *gr = g;
     856                 :          0 :         return !!g;
     857                 :            : }
     858                 :            : 
     859                 :            : #if ENABLE_GSHADOW
     860                 :          0 : int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
     861                 :            :         struct sgrp *s;
     862                 :            : 
     863         [ #  # ]:          0 :         assert(sg);
     864         [ #  # ]:          0 :         assert(stream);
     865                 :            : 
     866                 :          0 :         errno = 0;
     867                 :          0 :         s = fgetsgent(stream);
     868   [ #  #  #  # ]:          0 :         if (!s && errno != ENOENT)
     869                 :          0 :                 return errno_or_else(EIO);
     870                 :            : 
     871                 :          0 :         *sg = s;
     872                 :          0 :         return !!s;
     873                 :            : }
     874                 :            : #endif
     875                 :            : 
     876                 :          8 : int make_salt(char **ret) {
     877                 :            :         static const char table[] =
     878                 :            :                 "abcdefghijklmnopqrstuvwxyz"
     879                 :            :                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     880                 :            :                 "0123456789"
     881                 :            :                 "./";
     882                 :            : 
     883                 :            :         uint8_t raw[16];
     884                 :            :         char *salt, *j;
     885                 :            :         size_t i;
     886                 :            :         int r;
     887                 :            : 
     888                 :            :         /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
     889                 :            :          * SHA512, i.e. is legacy-free and minimizes our deps. */
     890                 :            : 
     891                 :            :         assert_cc(sizeof(table) == 64U + 1U);
     892                 :            : 
     893                 :            :         /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
     894                 :          8 :         r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
     895         [ -  + ]:          8 :         if (r < 0)
     896                 :          0 :                 return r;
     897                 :            : 
     898                 :          8 :         salt = new(char, 3+sizeof(raw)+1+1);
     899         [ -  + ]:          8 :         if (!salt)
     900                 :          0 :                 return -ENOMEM;
     901                 :            : 
     902                 :            :         /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
     903                 :          8 :         j = stpcpy(salt, "$6$");
     904         [ +  + ]:        136 :         for (i = 0; i < sizeof(raw); i++)
     905                 :        128 :                 j[i] = table[raw[i] & 63];
     906                 :          8 :         j[i++] = '$';
     907                 :          8 :         j[i] = 0;
     908                 :            : 
     909                 :          8 :         *ret = salt;
     910                 :          8 :         return 0;
     911                 :            : }

Generated by: LCOV version 1.14