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

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <nss.h>
       4                 :            : #include <pthread.h>
       5                 :            : 
       6                 :            : #include "sd-bus.h"
       7                 :            : 
       8                 :            : #include "alloc-util.h"
       9                 :            : #include "bus-common-errors.h"
      10                 :            : #include "dirent-util.h"
      11                 :            : #include "env-util.h"
      12                 :            : #include "fd-util.h"
      13                 :            : #include "format-util.h"
      14                 :            : #include "fs-util.h"
      15                 :            : #include "list.h"
      16                 :            : #include "macro.h"
      17                 :            : #include "nss-util.h"
      18                 :            : #include "signal-util.h"
      19                 :            : #include "stdio-util.h"
      20                 :            : #include "string-util.h"
      21                 :            : #include "user-util.h"
      22                 :            : #include "util.h"
      23                 :            : 
      24                 :            : #define DYNAMIC_USER_GECOS       "Dynamic User"
      25                 :            : #define DYNAMIC_USER_PASSWD      "*" /* locked */
      26                 :            : #define DYNAMIC_USER_DIR         "/"
      27                 :            : #define DYNAMIC_USER_SHELL       NOLOGIN
      28                 :            : 
      29                 :            : static const struct passwd root_passwd = {
      30                 :            :         .pw_name = (char*) "root",
      31                 :            :         .pw_passwd = (char*) "x", /* see shadow file */
      32                 :            :         .pw_uid = 0,
      33                 :            :         .pw_gid = 0,
      34                 :            :         .pw_gecos = (char*) "Super User",
      35                 :            :         .pw_dir = (char*) "/root",
      36                 :            :         .pw_shell = (char*) "/bin/sh",
      37                 :            : };
      38                 :            : 
      39                 :            : static const struct passwd nobody_passwd = {
      40                 :            :         .pw_name = (char*) NOBODY_USER_NAME,
      41                 :            :         .pw_passwd = (char*) "*", /* locked */
      42                 :            :         .pw_uid = UID_NOBODY,
      43                 :            :         .pw_gid = GID_NOBODY,
      44                 :            :         .pw_gecos = (char*) "User Nobody",
      45                 :            :         .pw_dir = (char*) "/",
      46                 :            :         .pw_shell = (char*) NOLOGIN,
      47                 :            : };
      48                 :            : 
      49                 :            : static const struct group root_group = {
      50                 :            :         .gr_name = (char*) "root",
      51                 :            :         .gr_gid = 0,
      52                 :            :         .gr_passwd = (char*) "x", /* see shadow file */
      53                 :            :         .gr_mem = (char*[]) { NULL },
      54                 :            : };
      55                 :            : 
      56                 :            : static const struct group nobody_group = {
      57                 :            :         .gr_name = (char*) NOBODY_GROUP_NAME,
      58                 :            :         .gr_gid = GID_NOBODY,
      59                 :            :         .gr_passwd = (char*) "*", /* locked */
      60                 :            :         .gr_mem = (char*[]) { NULL },
      61                 :            : };
      62                 :            : 
      63                 :            : typedef struct UserEntry UserEntry;
      64                 :            : typedef struct GetentData GetentData;
      65                 :            : 
      66                 :            : struct UserEntry {
      67                 :            :         uid_t id;
      68                 :            :         char *name;
      69                 :            : 
      70                 :            :         GetentData *data;
      71                 :            :         LIST_FIELDS(UserEntry, entries);
      72                 :            : };
      73                 :            : 
      74                 :            : struct GetentData {
      75                 :            :         /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really
      76                 :            :          * reentrant since it shares the reading position in the stream with all other threads',
      77                 :            :          * we need to protect the data in UserEntry from multithreaded programs which may call
      78                 :            :          * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the
      79                 :            :          * data by using the mutex below. */
      80                 :            :         pthread_mutex_t mutex;
      81                 :            : 
      82                 :            :         UserEntry *position;
      83                 :            :         LIST_HEAD(UserEntry, entries);
      84                 :            : };
      85                 :            : 
      86                 :            : static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
      87                 :            : static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
      88                 :            : 
      89                 :            : NSS_GETPW_PROTOTYPES(systemd);
      90                 :            : NSS_GETGR_PROTOTYPES(systemd);
      91                 :            : enum nss_status _nss_systemd_endpwent(void) _public_;
      92                 :            : enum nss_status _nss_systemd_setpwent(int stayopen) _public_;
      93                 :            : enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_;
      94                 :            : enum nss_status _nss_systemd_endgrent(void) _public_;
      95                 :            : enum nss_status _nss_systemd_setgrent(int stayopen) _public_;
      96                 :            : enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_;
      97                 :            : 
      98                 :          0 : static int direct_lookup_name(const char *name, uid_t *ret) {
      99                 :          0 :         _cleanup_free_ char *s = NULL;
     100                 :            :         const char *path;
     101                 :            :         int r;
     102                 :            : 
     103         [ #  # ]:          0 :         assert(name);
     104                 :            : 
     105                 :            :         /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
     106                 :            :          * namespace and subject to proper authentication. However, there's one problem: if our module is called from
     107                 :            :          * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
     108                 :            :          * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
     109                 :            : 
     110   [ #  #  #  #  :          0 :         path = strjoina("/run/systemd/dynamic-uid/direct:", name);
          #  #  #  #  #  
                #  #  # ]
     111                 :          0 :         r = readlink_malloc(path, &s);
     112         [ #  # ]:          0 :         if (r < 0)
     113                 :          0 :                 return r;
     114                 :            : 
     115                 :          0 :         return parse_uid(s, ret);
     116                 :            : }
     117                 :            : 
     118                 :          0 : static int direct_lookup_uid(uid_t uid, char **ret) {
     119                 :            :         char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
     120                 :            :         int r;
     121                 :            : 
     122         [ #  # ]:          0 :         xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
     123                 :            : 
     124                 :          0 :         r = readlink_malloc(path, &s);
     125         [ #  # ]:          0 :         if (r < 0)
     126                 :          0 :                 return r;
     127         [ #  # ]:          0 :         if (!valid_user_group_name(s)) { /* extra safety check */
     128                 :          0 :                 free(s);
     129                 :          0 :                 return -EINVAL;
     130                 :            :         }
     131                 :            : 
     132                 :          0 :         *ret = s;
     133                 :          0 :         return 0;
     134                 :            : }
     135                 :            : 
     136                 :          0 : enum nss_status _nss_systemd_getpwnam_r(
     137                 :            :                 const char *name,
     138                 :            :                 struct passwd *pwd,
     139                 :            :                 char *buffer, size_t buflen,
     140                 :            :                 int *errnop) {
     141                 :            : 
     142                 :          0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     143                 :          0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
     144                 :          0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     145                 :            :         uint32_t translated;
     146                 :            :         size_t l;
     147                 :            :         int bypass, r;
     148                 :            : 
     149                 :          0 :         PROTECT_ERRNO;
     150         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     151                 :            : 
     152         [ #  # ]:          0 :         assert(name);
     153         [ #  # ]:          0 :         assert(pwd);
     154                 :            : 
     155                 :            :         /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
     156                 :            :          * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
     157         [ #  # ]:          0 :         if (!valid_user_group_name(name))
     158                 :          0 :                 return NSS_STATUS_NOTFOUND;
     159                 :            : 
     160                 :            :         /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
     161         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
     162         [ #  # ]:          0 :                 if (streq(name, root_passwd.pw_name)) {
     163                 :          0 :                         *pwd = root_passwd;
     164                 :          0 :                         return NSS_STATUS_SUCCESS;
     165                 :            :                 }
     166         [ #  # ]:          0 :                 if (synthesize_nobody() &&
     167         [ #  # ]:          0 :                     streq(name, nobody_passwd.pw_name)) {
     168                 :          0 :                         *pwd = nobody_passwd;
     169                 :          0 :                         return NSS_STATUS_SUCCESS;
     170                 :            :                 }
     171                 :            :         }
     172                 :            : 
     173                 :            :         /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
     174         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
     175                 :          0 :                 return NSS_STATUS_NOTFOUND;
     176                 :            : 
     177                 :          0 :         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
     178         [ #  # ]:          0 :         if (bypass <= 0) {
     179                 :          0 :                 r = sd_bus_open_system(&bus);
     180         [ #  # ]:          0 :                 if (r < 0)
     181                 :          0 :                         bypass = 1;
     182                 :            :         }
     183                 :            : 
     184         [ #  # ]:          0 :         if (bypass > 0) {
     185                 :          0 :                 r = direct_lookup_name(name, (uid_t*) &translated);
     186         [ #  # ]:          0 :                 if (r == -ENOENT)
     187                 :          0 :                         return NSS_STATUS_NOTFOUND;
     188         [ #  # ]:          0 :                 if (r < 0)
     189                 :          0 :                         goto fail;
     190                 :            :         } else {
     191                 :          0 :                 r = sd_bus_call_method(bus,
     192                 :            :                                        "org.freedesktop.systemd1",
     193                 :            :                                        "/org/freedesktop/systemd1",
     194                 :            :                                        "org.freedesktop.systemd1.Manager",
     195                 :            :                                        "LookupDynamicUserByName",
     196                 :            :                                        &error,
     197                 :            :                                        &reply,
     198                 :            :                                        "s",
     199                 :            :                                        name);
     200         [ #  # ]:          0 :                 if (r < 0) {
     201         [ #  # ]:          0 :                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
     202                 :          0 :                                 return NSS_STATUS_NOTFOUND;
     203                 :            : 
     204                 :          0 :                         goto fail;
     205                 :            :                 }
     206                 :            : 
     207                 :          0 :                 r = sd_bus_message_read(reply, "u", &translated);
     208         [ #  # ]:          0 :                 if (r < 0)
     209                 :          0 :                         goto fail;
     210                 :            :         }
     211                 :            : 
     212                 :          0 :         l = strlen(name);
     213         [ #  # ]:          0 :         if (buflen < l+1) {
     214                 :          0 :                 UNPROTECT_ERRNO;
     215                 :          0 :                 *errnop = ERANGE;
     216                 :          0 :                 return NSS_STATUS_TRYAGAIN;
     217                 :            :         }
     218                 :            : 
     219                 :          0 :         memcpy(buffer, name, l+1);
     220                 :            : 
     221                 :          0 :         pwd->pw_name = buffer;
     222                 :          0 :         pwd->pw_uid = (uid_t) translated;
     223                 :          0 :         pwd->pw_gid = (uid_t) translated;
     224                 :          0 :         pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
     225                 :          0 :         pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
     226                 :          0 :         pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
     227                 :          0 :         pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
     228                 :            : 
     229                 :          0 :         return NSS_STATUS_SUCCESS;
     230                 :            : 
     231                 :          0 : fail:
     232                 :          0 :         UNPROTECT_ERRNO;
     233                 :          0 :         *errnop = -r;
     234                 :          0 :         return NSS_STATUS_UNAVAIL;
     235                 :            : }
     236                 :            : 
     237                 :          0 : enum nss_status _nss_systemd_getpwuid_r(
     238                 :            :                 uid_t uid,
     239                 :            :                 struct passwd *pwd,
     240                 :            :                 char *buffer, size_t buflen,
     241                 :            :                 int *errnop) {
     242                 :            : 
     243                 :          0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     244                 :          0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
     245                 :          0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     246                 :          0 :         _cleanup_free_ char *direct = NULL;
     247                 :            :         const char *translated;
     248                 :            :         size_t l;
     249                 :            :         int bypass, r;
     250                 :            : 
     251                 :          0 :         PROTECT_ERRNO;
     252         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     253                 :            : 
     254         [ #  # ]:          0 :         if (!uid_is_valid(uid))
     255                 :          0 :                 return NSS_STATUS_NOTFOUND;
     256                 :            : 
     257                 :            :         /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
     258         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
     259         [ #  # ]:          0 :                 if (uid == root_passwd.pw_uid) {
     260                 :          0 :                         *pwd = root_passwd;
     261                 :          0 :                         return NSS_STATUS_SUCCESS;
     262                 :            :                 }
     263         [ #  # ]:          0 :                 if (synthesize_nobody() &&
     264         [ #  # ]:          0 :                     uid == nobody_passwd.pw_uid) {
     265                 :          0 :                         *pwd = nobody_passwd;
     266                 :          0 :                         return NSS_STATUS_SUCCESS;
     267                 :            :                 }
     268                 :            :         }
     269                 :            : 
     270         [ #  # ]:          0 :         if (!uid_is_dynamic(uid))
     271                 :          0 :                 return NSS_STATUS_NOTFOUND;
     272                 :            : 
     273         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
     274                 :          0 :                 return NSS_STATUS_NOTFOUND;
     275                 :            : 
     276                 :          0 :         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
     277         [ #  # ]:          0 :         if (bypass <= 0) {
     278                 :          0 :                 r = sd_bus_open_system(&bus);
     279         [ #  # ]:          0 :                 if (r < 0)
     280                 :          0 :                         bypass = 1;
     281                 :            :         }
     282                 :            : 
     283         [ #  # ]:          0 :         if (bypass > 0) {
     284                 :          0 :                 r = direct_lookup_uid(uid, &direct);
     285         [ #  # ]:          0 :                 if (r == -ENOENT)
     286                 :          0 :                         return NSS_STATUS_NOTFOUND;
     287         [ #  # ]:          0 :                 if (r < 0)
     288                 :          0 :                         goto fail;
     289                 :            : 
     290                 :          0 :                 translated = direct;
     291                 :            : 
     292                 :            :         } else {
     293                 :          0 :                 r = sd_bus_call_method(bus,
     294                 :            :                                        "org.freedesktop.systemd1",
     295                 :            :                                        "/org/freedesktop/systemd1",
     296                 :            :                                        "org.freedesktop.systemd1.Manager",
     297                 :            :                                        "LookupDynamicUserByUID",
     298                 :            :                                        &error,
     299                 :            :                                        &reply,
     300                 :            :                                        "u",
     301                 :            :                                        (uint32_t) uid);
     302         [ #  # ]:          0 :                 if (r < 0) {
     303         [ #  # ]:          0 :                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
     304                 :          0 :                                 return NSS_STATUS_NOTFOUND;
     305                 :            : 
     306                 :          0 :                         goto fail;
     307                 :            :                 }
     308                 :            : 
     309                 :          0 :                 r = sd_bus_message_read(reply, "s", &translated);
     310         [ #  # ]:          0 :                 if (r < 0)
     311                 :          0 :                         goto fail;
     312                 :            :         }
     313                 :            : 
     314                 :          0 :         l = strlen(translated) + 1;
     315         [ #  # ]:          0 :         if (buflen < l) {
     316                 :          0 :                 UNPROTECT_ERRNO;
     317                 :          0 :                 *errnop = ERANGE;
     318                 :          0 :                 return NSS_STATUS_TRYAGAIN;
     319                 :            :         }
     320                 :            : 
     321                 :          0 :         memcpy(buffer, translated, l);
     322                 :            : 
     323                 :          0 :         pwd->pw_name = buffer;
     324                 :          0 :         pwd->pw_uid = uid;
     325                 :          0 :         pwd->pw_gid = uid;
     326                 :          0 :         pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
     327                 :          0 :         pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
     328                 :          0 :         pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
     329                 :          0 :         pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
     330                 :            : 
     331                 :          0 :         return NSS_STATUS_SUCCESS;
     332                 :            : 
     333                 :          0 : fail:
     334                 :          0 :         UNPROTECT_ERRNO;
     335                 :          0 :         *errnop = -r;
     336                 :          0 :         return NSS_STATUS_UNAVAIL;
     337                 :            : }
     338                 :            : 
     339                 :            : #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
     340                 :            : 
     341                 :          0 : enum nss_status _nss_systemd_getgrnam_r(
     342                 :            :                 const char *name,
     343                 :            :                 struct group *gr,
     344                 :            :                 char *buffer, size_t buflen,
     345                 :            :                 int *errnop) {
     346                 :            : 
     347                 :          0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     348                 :          0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
     349                 :          0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     350                 :            :         uint32_t translated;
     351                 :            :         size_t l;
     352                 :            :         int bypass, r;
     353                 :            : 
     354                 :          0 :         PROTECT_ERRNO;
     355         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     356                 :            : 
     357         [ #  # ]:          0 :         assert(name);
     358         [ #  # ]:          0 :         assert(gr);
     359                 :            : 
     360         [ #  # ]:          0 :         if (!valid_user_group_name(name))
     361                 :          0 :                 return NSS_STATUS_NOTFOUND;
     362                 :            : 
     363                 :            :         /* Synthesize records for root and nobody, in case they are missing form /etc/group */
     364         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
     365         [ #  # ]:          0 :                 if (streq(name, root_group.gr_name)) {
     366                 :          0 :                         *gr = root_group;
     367                 :          0 :                         return NSS_STATUS_SUCCESS;
     368                 :            :                 }
     369         [ #  # ]:          0 :                 if (synthesize_nobody() &&
     370         [ #  # ]:          0 :                     streq(name, nobody_group.gr_name)) {
     371                 :          0 :                         *gr = nobody_group;
     372                 :          0 :                         return NSS_STATUS_SUCCESS;
     373                 :            :                 }
     374                 :            :         }
     375                 :            : 
     376         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
     377                 :          0 :                 return NSS_STATUS_NOTFOUND;
     378                 :            : 
     379                 :          0 :         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
     380         [ #  # ]:          0 :         if (bypass <= 0) {
     381                 :          0 :                 r = sd_bus_open_system(&bus);
     382         [ #  # ]:          0 :                 if (r < 0)
     383                 :          0 :                         bypass = 1;
     384                 :            :         }
     385                 :            : 
     386         [ #  # ]:          0 :         if (bypass > 0) {
     387                 :          0 :                 r = direct_lookup_name(name, (uid_t*) &translated);
     388         [ #  # ]:          0 :                 if (r == -ENOENT)
     389                 :          0 :                         return NSS_STATUS_NOTFOUND;
     390         [ #  # ]:          0 :                 if (r < 0)
     391                 :          0 :                         goto fail;
     392                 :            :         } else {
     393                 :          0 :                 r = sd_bus_call_method(bus,
     394                 :            :                                        "org.freedesktop.systemd1",
     395                 :            :                                        "/org/freedesktop/systemd1",
     396                 :            :                                        "org.freedesktop.systemd1.Manager",
     397                 :            :                                        "LookupDynamicUserByName",
     398                 :            :                                        &error,
     399                 :            :                                        &reply,
     400                 :            :                                        "s",
     401                 :            :                                        name);
     402         [ #  # ]:          0 :                 if (r < 0) {
     403         [ #  # ]:          0 :                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
     404                 :          0 :                                 return NSS_STATUS_NOTFOUND;
     405                 :            : 
     406                 :          0 :                         goto fail;
     407                 :            :                 }
     408                 :            : 
     409                 :          0 :                 r = sd_bus_message_read(reply, "u", &translated);
     410         [ #  # ]:          0 :                 if (r < 0)
     411                 :          0 :                         goto fail;
     412                 :            :         }
     413                 :            : 
     414                 :          0 :         l = sizeof(char*) + strlen(name) + 1;
     415         [ #  # ]:          0 :         if (buflen < l) {
     416                 :          0 :                 UNPROTECT_ERRNO;
     417                 :          0 :                 *errnop = ERANGE;
     418                 :          0 :                 return NSS_STATUS_TRYAGAIN;
     419                 :            :         }
     420                 :            : 
     421         [ #  # ]:          0 :         memzero(buffer, sizeof(char*));
     422                 :          0 :         strcpy(buffer + sizeof(char*), name);
     423                 :            : 
     424                 :          0 :         gr->gr_name = buffer + sizeof(char*);
     425                 :          0 :         gr->gr_gid = (gid_t) translated;
     426                 :          0 :         gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
     427                 :          0 :         gr->gr_mem = (char**) buffer;
     428                 :            : 
     429                 :          0 :         return NSS_STATUS_SUCCESS;
     430                 :            : 
     431                 :          0 : fail:
     432                 :          0 :         UNPROTECT_ERRNO;
     433                 :          0 :         *errnop = -r;
     434                 :          0 :         return NSS_STATUS_UNAVAIL;
     435                 :            : }
     436                 :            : 
     437                 :          0 : enum nss_status _nss_systemd_getgrgid_r(
     438                 :            :                 gid_t gid,
     439                 :            :                 struct group *gr,
     440                 :            :                 char *buffer, size_t buflen,
     441                 :            :                 int *errnop) {
     442                 :            : 
     443                 :          0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     444                 :          0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
     445                 :          0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     446                 :          0 :         _cleanup_free_ char *direct = NULL;
     447                 :            :         const char *translated;
     448                 :            :         size_t l;
     449                 :            :         int bypass, r;
     450                 :            : 
     451                 :          0 :         PROTECT_ERRNO;
     452         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     453                 :            : 
     454         [ #  # ]:          0 :         if (!gid_is_valid(gid))
     455                 :          0 :                 return NSS_STATUS_NOTFOUND;
     456                 :            : 
     457                 :            :         /* Synthesize records for root and nobody, in case they are missing from /etc/group */
     458         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
     459         [ #  # ]:          0 :                 if (gid == root_group.gr_gid) {
     460                 :          0 :                         *gr = root_group;
     461                 :          0 :                         return NSS_STATUS_SUCCESS;
     462                 :            :                 }
     463         [ #  # ]:          0 :                 if (synthesize_nobody() &&
     464         [ #  # ]:          0 :                     gid == nobody_group.gr_gid) {
     465                 :          0 :                         *gr = nobody_group;
     466                 :          0 :                         return NSS_STATUS_SUCCESS;
     467                 :            :                 }
     468                 :            :         }
     469                 :            : 
     470         [ #  # ]:          0 :         if (!gid_is_dynamic(gid))
     471                 :          0 :                 return NSS_STATUS_NOTFOUND;
     472                 :            : 
     473         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
     474                 :          0 :                 return NSS_STATUS_NOTFOUND;
     475                 :            : 
     476                 :          0 :         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
     477         [ #  # ]:          0 :         if (bypass <= 0) {
     478                 :          0 :                 r = sd_bus_open_system(&bus);
     479         [ #  # ]:          0 :                 if (r < 0)
     480                 :          0 :                         bypass = 1;
     481                 :            :         }
     482                 :            : 
     483         [ #  # ]:          0 :         if (bypass > 0) {
     484                 :          0 :                 r = direct_lookup_uid(gid, &direct);
     485         [ #  # ]:          0 :                 if (r == -ENOENT)
     486                 :          0 :                         return NSS_STATUS_NOTFOUND;
     487         [ #  # ]:          0 :                 if (r < 0)
     488                 :          0 :                         goto fail;
     489                 :            : 
     490                 :          0 :                 translated = direct;
     491                 :            : 
     492                 :            :         } else {
     493                 :          0 :                 r = sd_bus_call_method(bus,
     494                 :            :                                        "org.freedesktop.systemd1",
     495                 :            :                                        "/org/freedesktop/systemd1",
     496                 :            :                                        "org.freedesktop.systemd1.Manager",
     497                 :            :                                        "LookupDynamicUserByUID",
     498                 :            :                                        &error,
     499                 :            :                                        &reply,
     500                 :            :                                        "u",
     501                 :            :                                        (uint32_t) gid);
     502         [ #  # ]:          0 :                 if (r < 0) {
     503         [ #  # ]:          0 :                         if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
     504                 :          0 :                                 return NSS_STATUS_NOTFOUND;
     505                 :            : 
     506                 :          0 :                         goto fail;
     507                 :            :                 }
     508                 :            : 
     509                 :          0 :                 r = sd_bus_message_read(reply, "s", &translated);
     510         [ #  # ]:          0 :                 if (r < 0)
     511                 :          0 :                         goto fail;
     512                 :            :         }
     513                 :            : 
     514                 :          0 :         l = sizeof(char*) + strlen(translated) + 1;
     515         [ #  # ]:          0 :         if (buflen < l) {
     516                 :          0 :                 UNPROTECT_ERRNO;
     517                 :          0 :                 *errnop = ERANGE;
     518                 :          0 :                 return NSS_STATUS_TRYAGAIN;
     519                 :            :         }
     520                 :            : 
     521         [ #  # ]:          0 :         memzero(buffer, sizeof(char*));
     522                 :          0 :         strcpy(buffer + sizeof(char*), translated);
     523                 :            : 
     524                 :          0 :         gr->gr_name = buffer + sizeof(char*);
     525                 :          0 :         gr->gr_gid = gid;
     526                 :          0 :         gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
     527                 :          0 :         gr->gr_mem = (char**) buffer;
     528                 :            : 
     529                 :          0 :         return NSS_STATUS_SUCCESS;
     530                 :            : 
     531                 :          0 : fail:
     532                 :          0 :         UNPROTECT_ERRNO;
     533                 :          0 :         *errnop = -r;
     534                 :          0 :         return NSS_STATUS_UNAVAIL;
     535                 :            : }
     536                 :            : 
     537                 :          0 : static void user_entry_free(UserEntry *p) {
     538         [ #  # ]:          0 :         if (!p)
     539                 :          0 :                 return;
     540                 :            : 
     541         [ #  # ]:          0 :         if (p->data)
     542   [ #  #  #  #  :          0 :                 LIST_REMOVE(entries, p->data->entries, p);
             #  #  #  # ]
     543                 :            : 
     544                 :          0 :         free(p->name);
     545                 :          0 :         free(p);
     546                 :            : }
     547                 :            : 
     548                 :          0 : static int user_entry_add(GetentData *data, const char *name, uid_t id) {
     549                 :            :         UserEntry *p;
     550                 :            : 
     551         [ #  # ]:          0 :         assert(data);
     552                 :            : 
     553                 :            :         /* This happens when User= or Group= already exists statically. */
     554         [ #  # ]:          0 :         if (!uid_is_dynamic(id))
     555                 :          0 :                 return -EINVAL;
     556                 :            : 
     557                 :          0 :         p = new0(UserEntry, 1);
     558         [ #  # ]:          0 :         if (!p)
     559                 :          0 :                 return -ENOMEM;
     560                 :            : 
     561                 :          0 :         p->name = strdup(name);
     562         [ #  # ]:          0 :         if (!p->name) {
     563                 :          0 :                 free(p);
     564                 :          0 :                 return -ENOMEM;
     565                 :            :         }
     566                 :          0 :         p->id = id;
     567                 :          0 :         p->data = data;
     568                 :            : 
     569   [ #  #  #  # ]:          0 :         LIST_PREPEND(entries, data->entries, p);
     570                 :            : 
     571                 :          0 :         return 0;
     572                 :            : }
     573                 :            : 
     574                 :          0 : static void systemd_endent(GetentData *data) {
     575                 :            :         UserEntry *p;
     576                 :            : 
     577         [ #  # ]:          0 :         assert(data);
     578                 :            : 
     579         [ #  # ]:          0 :         while ((p = data->entries))
     580                 :          0 :                 user_entry_free(p);
     581                 :            : 
     582                 :          0 :         data->position = NULL;
     583                 :          0 : }
     584                 :            : 
     585                 :          0 : static enum nss_status nss_systemd_endent(GetentData *p) {
     586                 :          0 :         PROTECT_ERRNO;
     587         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     588                 :            : 
     589         [ #  # ]:          0 :         assert_se(pthread_mutex_lock(&p->mutex) == 0);
     590                 :          0 :         systemd_endent(p);
     591         [ #  # ]:          0 :         assert_se(pthread_mutex_unlock(&p->mutex) == 0);
     592                 :            : 
     593                 :          0 :         return NSS_STATUS_SUCCESS;
     594                 :            : }
     595                 :            : 
     596                 :          0 : enum nss_status _nss_systemd_endpwent(void) {
     597                 :          0 :         return nss_systemd_endent(&getpwent_data);
     598                 :            : }
     599                 :            : 
     600                 :          0 : enum nss_status _nss_systemd_endgrent(void) {
     601                 :          0 :         return nss_systemd_endent(&getgrent_data);
     602                 :            : }
     603                 :            : 
     604                 :          0 : static int direct_enumeration(GetentData *p) {
     605                 :          0 :         _cleanup_closedir_ DIR *d = NULL;
     606                 :            :         struct dirent *de;
     607                 :            :         int r;
     608                 :            : 
     609         [ #  # ]:          0 :         assert(p);
     610                 :            : 
     611                 :          0 :         d = opendir("/run/systemd/dynamic-uid/");
     612         [ #  # ]:          0 :         if (!d)
     613                 :          0 :                 return -errno;
     614                 :            : 
     615   [ #  #  #  #  :          0 :         FOREACH_DIRENT(de, d, return -errno) {
                   #  # ]
     616      [ #  #  # ]:          0 :                 _cleanup_free_ char *name = NULL;
     617                 :            :                 uid_t uid, verified;
     618                 :            : 
     619         [ #  # ]:          0 :                 if (!dirent_is_file(de))
     620                 :          0 :                         continue;
     621                 :            : 
     622                 :          0 :                 r = parse_uid(de->d_name, &uid);
     623         [ #  # ]:          0 :                 if (r < 0)
     624                 :          0 :                         continue;
     625                 :            : 
     626                 :          0 :                 r = direct_lookup_uid(uid, &name);
     627         [ #  # ]:          0 :                 if (r == -ENOMEM)
     628                 :          0 :                         return r;
     629         [ #  # ]:          0 :                 if (r < 0)
     630                 :          0 :                         continue;
     631                 :            : 
     632                 :          0 :                 r = direct_lookup_name(name, &verified);
     633         [ #  # ]:          0 :                 if (r < 0)
     634                 :          0 :                         continue;
     635                 :            : 
     636         [ #  # ]:          0 :                 if (uid != verified)
     637                 :          0 :                         continue;
     638                 :            : 
     639                 :          0 :                 r = user_entry_add(p, name, uid);
     640         [ #  # ]:          0 :                 if (r == -ENOMEM)
     641                 :          0 :                         return r;
     642         [ #  # ]:          0 :                 if (r < 0)
     643                 :          0 :                         continue;
     644                 :            :         }
     645                 :            : 
     646                 :          0 :         return 0;
     647                 :            : }
     648                 :            : 
     649                 :          0 : static enum nss_status systemd_setent(GetentData *p) {
     650                 :          0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     651                 :          0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
     652                 :          0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     653                 :            :         const char *name;
     654                 :            :         uid_t id;
     655                 :            :         int bypass, r;
     656                 :            : 
     657                 :          0 :         PROTECT_ERRNO;
     658         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     659                 :            : 
     660         [ #  # ]:          0 :         assert(p);
     661                 :            : 
     662         [ #  # ]:          0 :         assert_se(pthread_mutex_lock(&p->mutex) == 0);
     663                 :            : 
     664                 :          0 :         systemd_endent(p);
     665                 :            : 
     666         [ #  # ]:          0 :         if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
     667                 :          0 :                 goto finish;
     668                 :            : 
     669                 :          0 :         bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
     670                 :            : 
     671         [ #  # ]:          0 :         if (bypass <= 0) {
     672                 :          0 :                 r = sd_bus_open_system(&bus);
     673         [ #  # ]:          0 :                 if (r < 0)
     674                 :          0 :                         bypass = 1;
     675                 :            :         }
     676                 :            : 
     677         [ #  # ]:          0 :         if (bypass > 0) {
     678                 :          0 :                 r = direct_enumeration(p);
     679         [ #  # ]:          0 :                 if (r < 0)
     680                 :          0 :                         goto fail;
     681                 :            : 
     682                 :          0 :                 goto finish;
     683                 :            :         }
     684                 :            : 
     685                 :          0 :         r = sd_bus_call_method(bus,
     686                 :            :                                "org.freedesktop.systemd1",
     687                 :            :                                "/org/freedesktop/systemd1",
     688                 :            :                                "org.freedesktop.systemd1.Manager",
     689                 :            :                                "GetDynamicUsers",
     690                 :            :                                &error,
     691                 :            :                                &reply,
     692                 :            :                                NULL);
     693         [ #  # ]:          0 :         if (r < 0)
     694                 :          0 :                 goto fail;
     695                 :            : 
     696                 :          0 :         r = sd_bus_message_enter_container(reply, 'a', "(us)");
     697         [ #  # ]:          0 :         if (r < 0)
     698                 :          0 :                 goto fail;
     699                 :            : 
     700         [ #  # ]:          0 :         while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) {
     701                 :          0 :                 r = user_entry_add(p, name, id);
     702         [ #  # ]:          0 :                 if (r == -ENOMEM)
     703                 :          0 :                         goto fail;
     704         [ #  # ]:          0 :                 if (r < 0)
     705                 :          0 :                         continue;
     706                 :            :         }
     707         [ #  # ]:          0 :         if (r < 0)
     708                 :          0 :                 goto fail;
     709                 :            : 
     710                 :          0 :         r = sd_bus_message_exit_container(reply);
     711         [ #  # ]:          0 :         if (r < 0)
     712                 :          0 :                 goto fail;
     713                 :            : 
     714                 :          0 : finish:
     715                 :          0 :         p->position = p->entries;
     716         [ #  # ]:          0 :         assert_se(pthread_mutex_unlock(&p->mutex) == 0);
     717                 :            : 
     718                 :          0 :         return NSS_STATUS_SUCCESS;
     719                 :            : 
     720                 :          0 : fail:
     721                 :          0 :         systemd_endent(p);
     722         [ #  # ]:          0 :         assert_se(pthread_mutex_unlock(&p->mutex) == 0);
     723                 :            : 
     724                 :          0 :         return NSS_STATUS_UNAVAIL;
     725                 :            : }
     726                 :            : 
     727                 :          0 : enum nss_status _nss_systemd_setpwent(int stayopen) {
     728                 :          0 :         return systemd_setent(&getpwent_data);
     729                 :            : }
     730                 :            : 
     731                 :          0 : enum nss_status _nss_systemd_setgrent(int stayopen) {
     732                 :          0 :         return systemd_setent(&getgrent_data);
     733                 :            : }
     734                 :            : 
     735                 :          0 : enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
     736                 :            :         enum nss_status ret;
     737                 :            :         UserEntry *p;
     738                 :            :         size_t len;
     739                 :            : 
     740                 :          0 :         PROTECT_ERRNO;
     741         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     742                 :            : 
     743         [ #  # ]:          0 :         assert(result);
     744         [ #  # ]:          0 :         assert(buffer);
     745         [ #  # ]:          0 :         assert(errnop);
     746                 :            : 
     747         [ #  # ]:          0 :         assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
     748                 :            : 
     749         [ #  # ]:          0 :         LIST_FOREACH(entries, p, getpwent_data.position) {
     750                 :          0 :                 len = strlen(p->name) + 1;
     751         [ #  # ]:          0 :                 if (buflen < len) {
     752                 :          0 :                         UNPROTECT_ERRNO;
     753                 :          0 :                         *errnop = ERANGE;
     754                 :          0 :                         ret = NSS_STATUS_TRYAGAIN;
     755                 :          0 :                         goto finalize;
     756                 :            :                 }
     757                 :            : 
     758                 :          0 :                 memcpy(buffer, p->name, len);
     759                 :            : 
     760                 :          0 :                 result->pw_name = buffer;
     761                 :          0 :                 result->pw_uid = p->id;
     762                 :          0 :                 result->pw_gid = p->id;
     763                 :          0 :                 result->pw_gecos = (char*) DYNAMIC_USER_GECOS;
     764                 :          0 :                 result->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
     765                 :          0 :                 result->pw_dir = (char*) DYNAMIC_USER_DIR;
     766                 :          0 :                 result->pw_shell = (char*) DYNAMIC_USER_SHELL;
     767                 :          0 :                 break;
     768                 :            :         }
     769         [ #  # ]:          0 :         if (!p) {
     770                 :          0 :                 ret = NSS_STATUS_NOTFOUND;
     771                 :          0 :                 goto finalize;
     772                 :            :         }
     773                 :            : 
     774                 :            :         /* On success, step to the next entry. */
     775                 :          0 :         p = p->entries_next;
     776                 :          0 :         ret = NSS_STATUS_SUCCESS;
     777                 :            : 
     778                 :          0 : finalize:
     779                 :            :         /* Save position for the next call. */
     780                 :          0 :         getpwent_data.position = p;
     781                 :            : 
     782         [ #  # ]:          0 :         assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
     783                 :            : 
     784                 :          0 :         return ret;
     785                 :            : }
     786                 :            : 
     787                 :          0 : enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) {
     788                 :            :         enum nss_status ret;
     789                 :            :         UserEntry *p;
     790                 :            :         size_t len;
     791                 :            : 
     792                 :          0 :         PROTECT_ERRNO;
     793         [ #  # ]:          0 :         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
     794                 :            : 
     795         [ #  # ]:          0 :         assert(result);
     796         [ #  # ]:          0 :         assert(buffer);
     797         [ #  # ]:          0 :         assert(errnop);
     798                 :            : 
     799         [ #  # ]:          0 :         assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
     800                 :            : 
     801         [ #  # ]:          0 :         LIST_FOREACH(entries, p, getgrent_data.position) {
     802                 :          0 :                 len = sizeof(char*) + strlen(p->name) + 1;
     803         [ #  # ]:          0 :                 if (buflen < len) {
     804                 :          0 :                         UNPROTECT_ERRNO;
     805                 :          0 :                         *errnop = ERANGE;
     806                 :          0 :                         ret = NSS_STATUS_TRYAGAIN;
     807                 :          0 :                         goto finalize;
     808                 :            :                 }
     809                 :            : 
     810         [ #  # ]:          0 :                 memzero(buffer, sizeof(char*));
     811                 :          0 :                 strcpy(buffer + sizeof(char*), p->name);
     812                 :            : 
     813                 :          0 :                 result->gr_name = buffer + sizeof(char*);
     814                 :          0 :                 result->gr_gid = p->id;
     815                 :          0 :                 result->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
     816                 :          0 :                 result->gr_mem = (char**) buffer;
     817                 :          0 :                 break;
     818                 :            :         }
     819         [ #  # ]:          0 :         if (!p) {
     820                 :          0 :                 ret = NSS_STATUS_NOTFOUND;
     821                 :          0 :                 goto finalize;
     822                 :            :         }
     823                 :            : 
     824                 :            :         /* On success, step to the next entry. */
     825                 :          0 :         p = p->entries_next;
     826                 :          0 :         ret = NSS_STATUS_SUCCESS;
     827                 :            : 
     828                 :          0 : finalize:
     829                 :            :         /* Save position for the next call. */
     830                 :          0 :         getgrent_data.position = p;
     831                 :            : 
     832         [ #  # ]:          0 :         assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
     833                 :            : 
     834                 :          0 :         return ret;
     835                 :            : }

Generated by: LCOV version 1.14