]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
user-util: add get{pw,gr}{uid,gid,name}_malloc() helpers 31011/head
authorLennart Poettering <lennart@poettering.net>
Fri, 19 Jan 2024 10:38:54 +0000 (11:38 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 22 Jan 2024 16:55:07 +0000 (17:55 +0100)
These are wrappers around getpwuid_r() and friends, and will allocate the
right-sized buffer for this call.

We so far had multiple implementations of a buffer allocation loop
around getpwuid_r() and friends, and they all suck in some way. Let's
clean this up and add a common implementation, and use it everywhere.

Also, be more careful with error numbers, in particular systematically
turn ENOENT into ENOSRCH (the former is what is returned if /etc/passwd
is absent, which we want to consider identical to user not existing,
which is ENOSRCH). We so far did this at some invocations, but not all.

There are some invocations of getpwuid() left in the codebase. We really
should fix those too, and have a single unified implementation of the
logic, but those are not as trivial to convert, so left for another
time.

src/basic/user-util.c
src/basic/user-util.h
src/core/dynamic-user.c
src/firstboot/firstboot.c
src/home/homed-manager-bus.c
src/home/homed-manager.c
src/login/logind-dbus.c
src/nspawn/nspawn.c
src/shared/user-record-nss.c
src/sysusers/sysusers.c

index ae6f13a8cf53ef10979f19edd0ee525a66b703f1..cee91541410fef4e138950ba1466f25bee28bc64 100644 (file)
@@ -431,31 +431,11 @@ char* uid_to_name(uid_t uid) {
                 return strdup(NOBODY_USER_NAME);
 
         if (uid_is_valid(uid)) {
-                long bufsize;
+                _cleanup_free_ struct passwd *pw = NULL;
 
-                bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
-                if (bufsize <= 0)
-                        bufsize = 4096;
-
-                for (;;) {
-                        struct passwd pwbuf, *pw = NULL;
-                        _cleanup_free_ char *buf = NULL;
-
-                        buf = malloc(bufsize);
-                        if (!buf)
-                                return NULL;
-
-                        r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
-                        if (r == 0 && pw)
-                                return strdup(pw->pw_name);
-                        if (r != ERANGE)
-                                break;
-
-                        if (bufsize > LONG_MAX/2) /* overflow check */
-                                return NULL;
-
-                        bufsize *= 2;
-                }
+                r = getpwuid_malloc(uid, &pw);
+                if (r >= 0)
+                        return strdup(pw->pw_name);
         }
 
         if (asprintf(&ret, UID_FMT, uid) < 0)
@@ -474,31 +454,11 @@ char* gid_to_name(gid_t gid) {
                 return strdup(NOBODY_GROUP_NAME);
 
         if (gid_is_valid(gid)) {
-                long bufsize;
+                _cleanup_free_ struct group *gr = NULL;
 
-                bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
-                if (bufsize <= 0)
-                        bufsize = 4096;
-
-                for (;;) {
-                        struct group grbuf, *gr = NULL;
-                        _cleanup_free_ char *buf = NULL;
-
-                        buf = malloc(bufsize);
-                        if (!buf)
-                                return NULL;
-
-                        r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
-                        if (r == 0 && gr)
-                                return strdup(gr->gr_name);
-                        if (r != ERANGE)
-                                break;
-
-                        if (bufsize > LONG_MAX/2) /* overflow check */
-                                return NULL;
-
-                        bufsize *= 2;
-                }
+                r = getgrgid_malloc(gid, &gr);
+                if (r >= 0)
+                        return strdup(gr->gr_name);
         }
 
         if (asprintf(&ret, GID_FMT, gid) < 0)
@@ -609,9 +569,10 @@ int getgroups_alloc(gid_t** gids) {
 }
 
 int get_home_dir(char **ret) {
-        struct passwd *p;
+        _cleanup_free_ struct passwd *p = NULL;
         const char *e;
         uid_t u;
+        int r;
 
         assert(ret);
 
@@ -626,19 +587,17 @@ int get_home_dir(char **ret) {
                 e = "/root";
                 goto found;
         }
-
         if (u == UID_NOBODY && synthesize_nobody()) {
                 e = "/";
                 goto found;
         }
 
         /* Check the database... */
-        errno = 0;
-        p = getpwuid(u);
-        if (!p)
-                return errno_or_else(ESRCH);
-        e = p->pw_dir;
+        r = getpwuid_malloc(u, &p);
+        if (r < 0)
+                return r;
 
+        e = p->pw_dir;
         if (!path_is_valid(e) || !path_is_absolute(e))
                 return -EINVAL;
 
@@ -647,9 +606,10 @@ int get_home_dir(char **ret) {
 }
 
 int get_shell(char **ret) {
-        struct passwd *p;
+        _cleanup_free_ struct passwd *p = NULL;
         const char *e;
         uid_t u;
+        int r;
 
         assert(ret);
 
@@ -670,12 +630,11 @@ int get_shell(char **ret) {
         }
 
         /* Check the database... */
-        errno = 0;
-        p = getpwuid(u);
-        if (!p)
-                return errno_or_else(ESRCH);
-        e = p->pw_shell;
+        r = getpwuid_malloc(u, &p);
+        if (r < 0)
+                return r;
 
+        e = p->pw_shell;
         if (!path_is_valid(e) || !path_is_absolute(e))
                 return -EINVAL;
 
@@ -1089,3 +1048,180 @@ const char* get_home_root(void) {
 
         return "/home";
 }
+
+static size_t getpw_buffer_size(void) {
+        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+        return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+static bool errno_is_user_doesnt_exist(int error) {
+        /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
+         * not found. */
+        return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
+}
+
+int getpwnam_malloc(const char *name, struct passwd **ret) {
+        size_t bufsize = getpw_buffer_size();
+        int r;
+
+        /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
+         * free() the returned sructured! */
+
+        if (isempty(name))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct passwd *pw = NULL;
+                r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+                if (r == 0) {
+                        if (pw) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.  For us that is equivalent to
+                 * the name not being defined. */
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret) {
+        size_t bufsize = getpw_buffer_size();
+        int r;
+
+        if (!uid_is_valid(uid))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct passwd *pw = NULL;
+                r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+                if (r == 0) {
+                        if (pw) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
+
+static size_t getgr_buffer_size(void) {
+        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+        return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+int getgrnam_malloc(const char *name, struct group **ret) {
+        size_t bufsize = getgr_buffer_size();
+        int r;
+
+        if (isempty(name))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct group *gr = NULL;
+                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+                if (r == 0) {
+                        if (gr) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
+
+int getgrgid_malloc(gid_t gid, struct group **ret) {
+        size_t bufsize = getgr_buffer_size();
+        int r;
+
+        if (!gid_is_valid(gid))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct group *gr = NULL;
+                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+                if (r == 0) {
+                        if (gr) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
index b363c2b28efc59e717461c550d63379d28bb6532..9d07ef31d22d852dcb93f1d717e2ff09d38aa2ef 100644 (file)
@@ -158,3 +158,9 @@ static inline bool hashed_password_is_locked_or_invalid(const char *password) {
  * Also see https://github.com/systemd/systemd/pull/24680#pullrequestreview-1439464325.
  */
 #define PASSWORD_UNPROVISIONED "!unprovisioned"
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret);
+int getpwnam_malloc(const char *name, struct passwd **ret);
+
+int getgrnam_malloc(const char *name, struct group **ret);
+int getgrgid_malloc(gid_t gid, struct group **ret);
index 484b0e29abdce7218c8d4eead231635b74cd9bb8..8462a31e5b541706c91fc06e0473232887a59d95 100644 (file)
@@ -293,8 +293,8 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
                 }
 
                 /* Some superficial check whether this UID/GID might already be taken by some static user */
-                if (getpwuid(candidate) ||
-                    getgrgid((gid_t) candidate) ||
+                if (getpwuid_malloc(candidate, /* ret= */ NULL) >= 0 ||
+                    getgrgid_malloc((gid_t) candidate, /* ret= */ NULL) >= 0 ||
                     search_ipc(candidate, (gid_t) candidate) != 0) {
                         (void) unlink(lock_path);
                         continue;
@@ -417,30 +417,26 @@ static int dynamic_user_realize(
                 /* First, let's parse this as numeric UID */
                 r = parse_uid(d->name, &num);
                 if (r < 0) {
-                        struct passwd *p;
-                        struct group *g;
+                        _cleanup_free_ struct passwd *p = NULL;
+                        _cleanup_free_ struct group *g = NULL;
 
                         if (is_user) {
                                 /* OK, this is not a numeric UID. Let's see if there's a user by this name */
-                                p = getpwnam(d->name);
-                                if (p) {
+                                if (getpwnam_malloc(d->name, &p) >= 0) {
                                         num = p->pw_uid;
                                         gid = p->pw_gid;
                                 } else {
                                         /* if the user does not exist but the group with the same name exists, refuse operation */
-                                        g = getgrnam(d->name);
-                                        if (g)
+                                        if (getgrnam_malloc(d->name, /* ret= */ NULL) >= 0)
                                                 return -EILSEQ;
                                 }
                         } else {
                                 /* Let's see if there's a group by this name */
-                                g = getgrnam(d->name);
-                                if (g)
+                                if (getgrnam_malloc(d->name, &g) >= 0)
                                         num = (uid_t) g->gr_gid;
                                 else {
                                         /* if the group does not exist but the user with the same name exists, refuse operation */
-                                        p = getpwnam(d->name);
-                                        if (p)
+                                        if (getpwnam_malloc(d->name, /* ret= */ NULL) >= 0)
                                                 return -EILSEQ;
                                 }
                         }
@@ -482,13 +478,12 @@ static int dynamic_user_realize(
                         uid_lock_fd = new_uid_lock_fd;
                 }
         } else if (is_user && !uid_is_dynamic(num)) {
-                struct passwd *p;
+                _cleanup_free_ struct passwd *p = NULL;
 
                 /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
-                errno = 0;
-                p = getpwuid(num);
-                if (!p)
-                        return errno_or_else(ESRCH);
+                r = getpwuid_malloc(num, &p);
+                if (r < 0)
+                        return r;
 
                 gid = p->pw_gid;
         }
index 18a7cb83b97d62a28e86a6c552900f2ced9495f7..652a6d5c89fe2d96714123f9973e7f5f310674ca 100644 (file)
@@ -1079,12 +1079,11 @@ static int process_root_account(int rfd) {
                 return log_error_errno(k, "Failed to check if directory file descriptor is root: %m");
 
         if (arg_copy_root_shell && k == 0) {
-                struct passwd *p;
+                _cleanup_free_ struct passwd *p = NULL;
 
-                errno = 0;
-                p = getpwnam("root");
-                if (!p)
-                        return log_error_errno(errno_or_else(EIO), "Failed to find passwd entry for root: %m");
+                r = getpwnam_malloc("root", &p);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to find passwd entry for root: %m");
 
                 r = free_and_strdup(&arg_root_shell, p->pw_shell);
                 if (r < 0)
index 5395ae62c1b7fc70f6d2bcecfaea200a1ca1a26e..d45e0f123855ae543c8922b1f0d785e2a810b195 100644 (file)
@@ -342,8 +342,6 @@ static int method_deactivate_home(sd_bus_message *message, void *userdata, sd_bu
 
 static int validate_and_allocate_home(Manager *m, UserRecord *hr, Home **ret, sd_bus_error *error) {
         _cleanup_(user_record_unrefp) UserRecord *signed_hr = NULL;
-        struct passwd *pw;
-        struct group *gr;
         bool signed_locally;
         Home *other;
         int r;
@@ -360,13 +358,17 @@ static int validate_and_allocate_home(Manager *m, UserRecord *hr, Home **ret, sd
         if (other)
                 return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists already, refusing.", hr->user_name);
 
-        pw = getpwnam(hr->user_name);
-        if (pw)
+        r = getpwnam_malloc(hr->user_name, /* ret= */ NULL);
+        if (r >= 0)
                 return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists in the NSS user database, refusing.", hr->user_name);
+        if (r != -ESRCH)
+                return r;
 
-        gr = getgrnam(hr->user_name);
-        if (gr)
+        r = getgrnam_malloc(hr->user_name, /* ret= */ NULL);
+        if (r >= 0)
                 return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s conflicts with an NSS group by the same name, refusing.", hr->user_name);
+        if (r != -ESRCH)
+                return r;
 
         r = manager_verify_user_record(m, hr);
         switch (r) {
@@ -397,17 +399,24 @@ static int validate_and_allocate_home(Manager *m, UserRecord *hr, Home **ret, sd
         }
 
         if (uid_is_valid(hr->uid)) {
+                _cleanup_free_ struct passwd *pw = NULL;
+                _cleanup_free_ struct group *gr = NULL;
+
                 other = hashmap_get(m->homes_by_uid, UID_TO_PTR(hr->uid));
                 if (other)
                         return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by home %s, refusing.", hr->uid, other->user_name);
 
-                pw = getpwuid(hr->uid);
-                if (pw)
+                r = getpwuid_malloc(hr->uid, &pw);
+                if (r >= 0)
                         return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by NSS user %s, refusing.", hr->uid, pw->pw_name);
+                if (r != -ESRCH)
+                        return r;
 
-                gr = getgrgid(hr->uid);
-                if (gr)
+                r = getgrgid_malloc(hr->uid, &gr);
+                if (r >= 0)
                         return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use as GID by NSS group %s, refusing.", hr->uid, gr->gr_name);
+                if (r != -ESRCH)
+                        return r;
         } else {
                 r = manager_augment_record_with_uid(m, hr);
                 if (r < 0)
index b1d0c5112031ef7ba419ba984ddb2ad046c1b92a..810ecb23fcfc843b0334cc87a0d4788429c2432b 100644 (file)
@@ -588,8 +588,8 @@ static int manager_acquire_uid(
         assert(ret);
 
         for (;;) {
-                struct passwd *pw;
-                struct group *gr;
+                _cleanup_free_ struct passwd *pw = NULL;
+                _cleanup_free_ struct group *gr = NULL;
                 uid_t candidate;
                 Home *other;
 
@@ -632,19 +632,27 @@ static int manager_acquire_uid(
                         continue;
                 }
 
-                pw = getpwuid(candidate);
-                if (pw) {
+                r = getpwuid_malloc(candidate, &pw);
+                if (r >= 0) {
                         log_debug("Candidate UID " UID_FMT " already registered by another user in NSS (%s), let's try another.",
                                   candidate, pw->pw_name);
                         continue;
                 }
+                if (r != -ESRCH) {
+                        log_debug_errno(r, "Failed to check if an NSS user is already registered for candidate UID " UID_FMT ", assuming there might be: %m", candidate);
+                        continue;
+                }
 
-                gr = getgrgid((gid_t) candidate);
-                if (gr) {
+                r = getgrgid_malloc((gid_t) candidate, &gr);
+                if (r >= 0) {
                         log_debug("Candidate UID " UID_FMT " already registered by another group in NSS (%s), let's try another.",
                                   candidate, gr->gr_name);
                         continue;
                 }
+                if (r != -ESRCH) {
+                        log_debug_errno(r, "Failed to check if an NSS group is already registered for candidate UID " UID_FMT ", assuming there might be: %m", candidate);
+                        continue;
+                }
 
                 r = search_ipc(candidate, (gid_t) candidate);
                 if (r < 0)
@@ -715,7 +723,7 @@ static int manager_add_home_by_image(
                 }
         } else {
                 /* Check NSS, in case there's another user or group by this name */
-                if (getpwnam(user_name) || getgrnam(user_name)) {
+                if (getpwnam_malloc(user_name, /* ret= */ NULL) >= 0 || getgrnam_malloc(user_name, /* ret= */ NULL) >= 0) {
                         log_debug("Found an existing user or group by name '%s', ignoring image '%s'.", user_name, image_path);
                         return 0;
                 }
index 8f70253ef6342c1aee0326c7257a1d5dde12d242..3bb8527703f081668caaf2c98ff66a0144f97f4b 100644 (file)
@@ -1419,10 +1419,10 @@ static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus
 
 static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+        _cleanup_free_ struct passwd *pw = NULL;
         _cleanup_free_ char *cc = NULL;
         Manager *m = ASSERT_PTR(userdata);
         int r, b, interactive;
-        struct passwd *pw;
         const char *path;
         uint32_t uid, auth_uid;
 
@@ -1450,10 +1450,9 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
         if (r < 0)
                 return r;
 
-        errno = 0;
-        pw = getpwuid(uid);
-        if (!pw)
-                return errno_or_else(ENOENT);
+        r = getpwuid_malloc(uid, &pw);
+        if (r < 0)
+                return r;
 
         r = bus_verify_polkit_async_full(
                         message,
index c48b51323b7ff98e4225e21235aa9f5d4e49b9f3..89ef0e4daaa040385e45104918108de735a78a4c 100644 (file)
@@ -4211,13 +4211,13 @@ static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) {
                         return r;
 
                 /* Make some superficial checks whether the range is currently known in the user database */
-                if (getpwuid(candidate))
+                if (getpwuid_malloc(candidate, /* ret= */ NULL) >= 0)
                         goto next;
-                if (getpwuid(candidate + UINT32_C(0xFFFE)))
+                if (getpwuid_malloc(candidate + UINT32_C(0xFFFE), /* ret= */ NULL) >= 0)
                         goto next;
-                if (getgrgid(candidate))
+                if (getgrgid_malloc(candidate, /* ret= */ NULL) >= 0)
                         goto next;
-                if (getgrgid(candidate + UINT32_C(0xFFFE)))
+                if (getgrgid_malloc(candidate + UINT32_C(0xFFFE), /* ret= */ NULL) >= 0)
                         goto next;
 
                 *ret_lock_file = lf;
index 414a49331b106a6033d0235f61ffeea126343c75..ffb572146628d1dd2fa13102751a9f5c5c1bee64 100644 (file)
@@ -208,39 +208,17 @@ int nss_user_record_by_name(
                 bool with_shadow,
                 UserRecord **ret) {
 
-        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
-        struct passwd pwd, *result;
+        _cleanup_free_ char *sbuf = NULL;
+        _cleanup_free_ struct passwd *result = NULL;
         bool incomplete = false;
-        size_t buflen = 4096;
         struct spwd spwd, *sresult = NULL;
         int r;
 
         assert(name);
 
-        for (;;) {
-                buf = malloc(buflen);
-                if (!buf)
-                        return -ENOMEM;
-
-                r = getpwnam_r(name, &pwd, buf, buflen, &result);
-                if (r == 0)  {
-                        if (!result)
-                                return -ESRCH;
-
-                        break;
-                }
-
-                if (r < 0)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getpwnam_r() returned a negative value");
-                if (r != ERANGE)
-                        return -r;
-
-                if (buflen > SIZE_MAX / 2)
-                        return -ERANGE;
-
-                buflen *= 2;
-                buf = mfree(buf);
-        }
+        r = getpwnam_malloc(name, &result);
+        if (r < 0)
+                return r;
 
         if (with_shadow) {
                 r = nss_spwd_for_passwd(result, &spwd, &sbuf);
@@ -266,36 +244,15 @@ int nss_user_record_by_uid(
                 bool with_shadow,
                 UserRecord **ret) {
 
-        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
-        struct passwd pwd, *result;
+        _cleanup_free_ char *sbuf = NULL;
+        _cleanup_free_ struct passwd *result = NULL;
         bool incomplete = false;
-        size_t buflen = 4096;
         struct spwd spwd, *sresult = NULL;
         int r;
 
-        for (;;) {
-                buf = malloc(buflen);
-                if (!buf)
-                        return -ENOMEM;
-
-                r = getpwuid_r(uid, &pwd, buf, buflen, &result);
-                if (r == 0)  {
-                        if (!result)
-                                return -ESRCH;
-
-                        break;
-                }
-                if (r < 0)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getpwuid_r() returned a negative value");
-                if (r != ERANGE)
-                        return -r;
-
-                if (buflen > SIZE_MAX / 2)
-                        return -ERANGE;
-
-                buflen *= 2;
-                buf = mfree(buf);
-        }
+        r = getpwuid_malloc(uid, &result);
+        if (r < 0)
+                return r;
 
         if (with_shadow)  {
                 r = nss_spwd_for_passwd(result, &spwd, &sbuf);
@@ -422,38 +379,17 @@ int nss_group_record_by_name(
                 bool with_shadow,
                 GroupRecord **ret) {
 
-        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
-        struct group grp, *result;
+        _cleanup_free_ char *sbuf = NULL;
+        _cleanup_free_ struct group *result = NULL;
         bool incomplete = false;
-        size_t buflen = 4096;
         struct sgrp sgrp, *sresult = NULL;
         int r;
 
         assert(name);
 
-        for (;;) {
-                buf = malloc(buflen);
-                if (!buf)
-                        return -ENOMEM;
-
-                r = getgrnam_r(name, &grp, buf, buflen, &result);
-                if (r == 0)  {
-                        if (!result)
-                                return -ESRCH;
-
-                        break;
-                }
-
-                if (r < 0)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrnam_r() returned a negative value");
-                if (r != ERANGE)
-                        return -r;
-                if (buflen > SIZE_MAX / 2)
-                        return -ERANGE;
-
-                buflen *= 2;
-                buf = mfree(buf);
-        }
+        r = getgrnam_malloc(name, &result);
+        if (r < 0)
+                return r;
 
         if (with_shadow) {
                 r = nss_sgrp_for_group(result, &sgrp, &sbuf);
@@ -479,35 +415,15 @@ int nss_group_record_by_gid(
                 bool with_shadow,
                 GroupRecord **ret) {
 
-        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
-        struct group grp, *result;
+        _cleanup_free_ char *sbuf = NULL;
+        _cleanup_free_ struct group *result = NULL;
         bool incomplete = false;
-        size_t buflen = 4096;
         struct sgrp sgrp, *sresult = NULL;
         int r;
 
-        for (;;) {
-                buf = malloc(buflen);
-                if (!buf)
-                        return -ENOMEM;
-
-                r = getgrgid_r(gid, &grp, buf, buflen, &result);
-                if (r == 0)  {
-                        if (!result)
-                                return -ESRCH;
-                        break;
-                }
-
-                if (r < 0)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrgid_r() returned a negative value");
-                if (r != ERANGE)
-                        return -r;
-                if (buflen > SIZE_MAX / 2)
-                        return -ERANGE;
-
-                buflen *= 2;
-                buf = mfree(buf);
-        }
+        r = getgrgid_malloc(gid, &result);
+        if (r < 0)
+                return r;
 
         if (with_shadow) {
                 r = nss_sgrp_for_group(result, &sgrp, &sbuf);
index 4d5d61c691513652091d233e7935d66bd4d7d8b0..96fb6f8ad556e49b6fab7a02968ba1c940a01053 100644 (file)
@@ -141,12 +141,6 @@ static void context_done(Context *c) {
         uid_range_free(c->uid_range);
 }
 
-static int errno_is_not_exists(int code) {
-        /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
-         * not found. */
-        return IN_SET(code, 0, ENOENT, ESRCH, EBADF, EPERM);
-}
-
 /* Note: the lifetime of the compound literal is the immediately surrounding block,
  * see C11 ยง6.5.2.5, and
  * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
@@ -1028,6 +1022,7 @@ static int uid_is_ok(
                 const char *name,
                 bool check_with_gid) {
 
+        int r;
         assert(c);
 
         /* Let's see if we already have assigned the UID a second time */
@@ -1058,24 +1053,21 @@ static int uid_is_ok(
 
         /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
         if (!arg_root) {
-                struct passwd *p;
-                struct group *g;
+                _cleanup_free_ struct group *g = NULL;
 
-                errno = 0;
-                p = getpwuid(uid);
-                if (p)
+                r = getpwuid_malloc(uid, /* ret= */ NULL);
+                if (r >= 0)
                         return 0;
-                if (!IN_SET(errno, 0, ENOENT))
-                        return -errno;
+                if (r != -ESRCH)
+                        return r;
 
                 if (check_with_gid) {
-                        errno = 0;
-                        g = getgrgid((gid_t) uid);
-                        if (g) {
+                        r = getgrgid_malloc((gid_t) uid, &g);
+                        if (r >= 0) {
                                 if (!streq(g->gr_name, name))
                                         return 0;
-                        } else if (!IN_SET(errno, 0, ENOENT))
-                                return -errno;
+                        } else if (r != -ESRCH)
+                                return r;
                 }
         }
 
@@ -1164,12 +1156,11 @@ static int add_user(Context *c, Item *i) {
         }
 
         if (!arg_root) {
-                struct passwd *p;
+                _cleanup_free_ struct passwd *p = NULL;
 
                 /* Also check NSS */
-                errno = 0;
-                p = getpwnam(i->name);
-                if (p) {
+                r = getpwnam_malloc(i->name, &p);
+                if (r >= 0) {
                         log_debug("User %s already exists.", i->name);
                         i->uid = p->pw_uid;
                         i->uid_set = true;
@@ -1180,8 +1171,8 @@ static int add_user(Context *c, Item *i) {
 
                         return 0;
                 }
-                if (!errno_is_not_exists(errno))
-                        return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
+                if (r != -ESRCH)
+                        return log_error_errno(r, "Failed to check if user %s already exists: %m", i->name);
         }
 
         /* Try to use the suggested numeric UID */
@@ -1270,10 +1261,9 @@ static int gid_is_ok(
                 const char *groupname,
                 bool check_with_uid) {
 
-        struct group *g;
-        struct passwd *p;
         Item *user;
         char *username;
+        int r;
 
         assert(c);
         assert(groupname);
@@ -1298,20 +1288,18 @@ static int gid_is_ok(
         }
 
         if (!arg_root) {
-                errno = 0;
-                g = getgrgid(gid);
-                if (g)
+                r = getgrgid_malloc(gid, /* ret= */ NULL);
+                if (r >= 0)
                         return 0;
-                if (!IN_SET(errno, 0, ENOENT))
-                        return -errno;
+                if (r != -ESRCH)
+                        return r;
 
                 if (check_with_uid) {
-                        errno = 0;
-                        p = getpwuid((uid_t) gid);
-                        if (p)
+                        r = getpwuid_malloc(gid, /* ret= */ NULL);
+                        if (r >= 0)
                                 return 0;
-                        if (!IN_SET(errno, 0, ENOENT))
-                                return -errno;
+                        if (r != -ESRCH)
+                                return r;
                 }
         }
 
@@ -1324,6 +1312,7 @@ static int get_gid_by_name(
                 gid_t *ret_gid) {
 
         void *z;
+        int r;
 
         assert(c);
         assert(ret_gid);
@@ -1337,16 +1326,15 @@ static int get_gid_by_name(
 
         /* Also check NSS */
         if (!arg_root) {
-                struct group *g;
+                _cleanup_free_ struct group *g = NULL;
 
-                errno = 0;
-                g = getgrnam(name);
-                if (g) {
+                r = getgrnam_malloc(name, &g);
+                if (r >= 0) {
                         *ret_gid = g->gr_gid;
                         return 0;
                 }
-                if (!errno_is_not_exists(errno))
-                        return log_error_errno(errno, "Failed to check if group %s already exists: %m", name);
+                if (r != -ESRCH)
+                        return log_error_errno(r, "Failed to check if group %s already exists: %m", name);
         }
 
         return -ENOENT;