| File: | build-scan/../src/basic/user-util.c |
| Warning: | line 410, column 16 1st function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <alloca.h> | |||
| 4 | #include <errno(*__errno_location ()).h> | |||
| 5 | #include <fcntl.h> | |||
| 6 | #include <grp.h> | |||
| 7 | #include <pwd.h> | |||
| 8 | #include <stddef.h> | |||
| 9 | #include <stdint.h> | |||
| 10 | #include <stdio.h> | |||
| 11 | #include <stdlib.h> | |||
| 12 | #include <string.h> | |||
| 13 | #include <sys/stat.h> | |||
| 14 | #include <unistd.h> | |||
| 15 | #include <utmp.h> | |||
| 16 | ||||
| 17 | #include "sd-messages.h" | |||
| 18 | ||||
| 19 | #include "alloc-util.h" | |||
| 20 | #include "fd-util.h" | |||
| 21 | #include "fileio.h" | |||
| 22 | #include "format-util.h" | |||
| 23 | #include "macro.h" | |||
| 24 | #include "missing.h" | |||
| 25 | #include "parse-util.h" | |||
| 26 | #include "path-util.h" | |||
| 27 | #include "string-util.h" | |||
| 28 | #include "strv.h" | |||
| 29 | #include "user-util.h" | |||
| 30 | #include "utf8.h" | |||
| 31 | ||||
| 32 | bool_Bool uid_is_valid(uid_t uid) { | |||
| 33 | ||||
| 34 | /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */ | |||
| 35 | ||||
| 36 | /* Some libc APIs use UID_INVALID as special placeholder */ | |||
| 37 | if (uid == (uid_t) UINT32_C(0xFFFFFFFF)0xFFFFFFFFU) | |||
| 38 | return false0; | |||
| 39 | ||||
| 40 | /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ | |||
| 41 | if (uid == (uid_t) UINT32_C(0xFFFF)0xFFFFU) | |||
| 42 | return false0; | |||
| 43 | ||||
| 44 | return true1; | |||
| 45 | } | |||
| 46 | ||||
| 47 | int parse_uid(const char *s, uid_t *ret) { | |||
| 48 | uint32_t uid = 0; | |||
| 49 | int r; | |||
| 50 | ||||
| 51 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/basic/user-util.c", 51, __PRETTY_FUNCTION__ ); } while (0); | |||
| 52 | ||||
| 53 | assert_cc(sizeof(uid_t) == sizeof(uint32_t))GCC diagnostic push
; GCC diagnostic ignored "-Wdeclaration-after-statement" ; struct _assert_struct_11 { char x[(sizeof(uid_t) == sizeof (uint32_t)) ? 0 : -1]; }; GCC diagnostic pop ; | |||
| 54 | ||||
| 55 | /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and | |||
| 56 | * whitespace. We do this, since this call is often used in a context where we parse things as UID | |||
| 57 | * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs | |||
| 58 | * are parsed as UIDs only if they really really look like UIDs. */ | |||
| 59 | r = safe_atou32_full(s, 10 | |||
| 60 | | SAFE_ATO_REFUSE_PLUS_MINUS(1U << 30) | |||
| 61 | | SAFE_ATO_REFUSE_LEADING_ZERO(1U << 29) | |||
| 62 | | SAFE_ATO_REFUSE_LEADING_WHITESPACE(1U << 28), &uid); | |||
| 63 | if (r < 0) | |||
| 64 | return r; | |||
| 65 | ||||
| 66 | if (!uid_is_valid(uid)) | |||
| 67 | return -ENXIO6; /* we return ENXIO instead of EINVAL | |||
| 68 | * here, to make it easy to distuingish | |||
| 69 | * invalid numeric uids from invalid | |||
| 70 | * strings. */ | |||
| 71 | ||||
| 72 | if (ret) | |||
| 73 | *ret = uid; | |||
| 74 | ||||
| 75 | return 0; | |||
| 76 | } | |||
| 77 | ||||
| 78 | char* getlogname_malloc(void) { | |||
| 79 | uid_t uid; | |||
| 80 | struct stat st; | |||
| 81 | ||||
| 82 | if (isatty(STDIN_FILENO0) && fstat(STDIN_FILENO0, &st) >= 0) | |||
| 83 | uid = st.st_uid; | |||
| 84 | else | |||
| 85 | uid = getuid(); | |||
| 86 | ||||
| 87 | return uid_to_name(uid); | |||
| 88 | } | |||
| 89 | ||||
| 90 | char *getusername_malloc(void) { | |||
| 91 | const char *e; | |||
| 92 | ||||
| 93 | e = getenv("USER"); | |||
| 94 | if (e) | |||
| 95 | return strdup(e); | |||
| 96 | ||||
| 97 | return uid_to_name(getuid()); | |||
| 98 | } | |||
| 99 | ||||
| 100 | int get_user_creds( | |||
| 101 | const char **username, | |||
| 102 | uid_t *uid, gid_t *gid, | |||
| 103 | const char **home, | |||
| 104 | const char **shell) { | |||
| 105 | ||||
| 106 | struct passwd *p; | |||
| 107 | uid_t u; | |||
| 108 | ||||
| 109 | assert(username)do { if ((__builtin_expect(!!(!(username)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("username"), "../src/basic/user-util.c", 109, __PRETTY_FUNCTION__); } while (0); | |||
| 110 | assert(*username)do { if ((__builtin_expect(!!(!(*username)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("*username"), "../src/basic/user-util.c" , 110, __PRETTY_FUNCTION__); } while (0); | |||
| 111 | ||||
| 112 | /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode | |||
| 113 | * their user record data. */ | |||
| 114 | ||||
| 115 | if (STR_IN_SET(*username, "root", "0")(!!strv_find((((char**) ((const char*[]) { "root", "0", ((void *)0) }))), (*username)))) { | |||
| 116 | *username = "root"; | |||
| 117 | ||||
| 118 | if (uid) | |||
| 119 | *uid = 0; | |||
| 120 | if (gid) | |||
| 121 | *gid = 0; | |||
| 122 | ||||
| 123 | if (home) | |||
| 124 | *home = "/root"; | |||
| 125 | ||||
| 126 | if (shell) | |||
| 127 | *shell = "/bin/sh"; | |||
| 128 | ||||
| 129 | return 0; | |||
| 130 | } | |||
| 131 | ||||
| 132 | if (synthesize_nobody() && | |||
| 133 | STR_IN_SET(*username, NOBODY_USER_NAME, "65534")(!!strv_find((((char**) ((const char*[]) { "nobody", "65534", ((void*)0) }))), (*username)))) { | |||
| 134 | *username = NOBODY_USER_NAME"nobody"; | |||
| 135 | ||||
| 136 | if (uid) | |||
| 137 | *uid = UID_NOBODY((uid_t) 65534U); | |||
| 138 | if (gid) | |||
| 139 | *gid = GID_NOBODY((gid_t) 65534U); | |||
| 140 | ||||
| 141 | if (home) | |||
| 142 | *home = "/"; | |||
| 143 | ||||
| 144 | if (shell) | |||
| 145 | *shell = "/sbin/nologin"; | |||
| 146 | ||||
| 147 | return 0; | |||
| 148 | } | |||
| 149 | ||||
| 150 | if (parse_uid(*username, &u) >= 0) { | |||
| 151 | errno(*__errno_location ()) = 0; | |||
| 152 | p = getpwuid(u); | |||
| 153 | ||||
| 154 | /* If there are multiple users with the same id, make | |||
| 155 | * sure to leave $USER to the configured value instead | |||
| 156 | * of the first occurrence in the database. However if | |||
| 157 | * the uid was configured by a numeric uid, then let's | |||
| 158 | * pick the real username from /etc/passwd. */ | |||
| 159 | if (p) | |||
| 160 | *username = p->pw_name; | |||
| 161 | } else { | |||
| 162 | errno(*__errno_location ()) = 0; | |||
| 163 | p = getpwnam(*username); | |||
| 164 | } | |||
| 165 | ||||
| 166 | if (!p) | |||
| 167 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
| 168 | ||||
| 169 | if (uid) { | |||
| 170 | if (!uid_is_valid(p->pw_uid)) | |||
| 171 | return -EBADMSG74; | |||
| 172 | ||||
| 173 | *uid = p->pw_uid; | |||
| 174 | } | |||
| 175 | ||||
| 176 | if (gid) { | |||
| 177 | if (!gid_is_valid(p->pw_gid)) | |||
| 178 | return -EBADMSG74; | |||
| 179 | ||||
| 180 | *gid = p->pw_gid; | |||
| 181 | } | |||
| 182 | ||||
| 183 | if (home) | |||
| 184 | *home = p->pw_dir; | |||
| 185 | ||||
| 186 | if (shell) | |||
| 187 | *shell = p->pw_shell; | |||
| 188 | ||||
| 189 | return 0; | |||
| 190 | } | |||
| 191 | ||||
| 192 | static inline bool_Bool is_nologin_shell(const char *shell) { | |||
| 193 | ||||
| 194 | return PATH_IN_SET(shell,({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 195 | /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 196 | * message and exits. Different distributions place the binary at different places though,({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 197 | * hence let's list them all. */({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 198 | "/bin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 199 | "/sbin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 200 | "/usr/bin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 201 | "/usr/sbin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 202 | /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 203 | * any message printing. Different distributions place the binary at various places but at({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 204 | * least not in the 'sbin' directory. */({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 205 | "/bin/false",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 206 | "/usr/bin/false",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 207 | "/bin/true",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
| 208 | "/usr/bin/true")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }); | |||
| 209 | } | |||
| 210 | ||||
| 211 | int get_user_creds_clean( | |||
| 212 | const char **username, | |||
| 213 | uid_t *uid, gid_t *gid, | |||
| 214 | const char **home, | |||
| 215 | const char **shell) { | |||
| 216 | ||||
| 217 | int r; | |||
| 218 | ||||
| 219 | /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */ | |||
| 220 | ||||
| 221 | r = get_user_creds(username, uid, gid, home, shell); | |||
| 222 | if (r < 0) | |||
| 223 | return r; | |||
| 224 | ||||
| 225 | if (shell && | |||
| 226 | (isempty(*shell) || is_nologin_shell(*shell))) | |||
| 227 | *shell = NULL((void*)0); | |||
| 228 | ||||
| 229 | if (home && empty_or_root(*home)) | |||
| 230 | *home = NULL((void*)0); | |||
| 231 | ||||
| 232 | return 0; | |||
| 233 | } | |||
| 234 | ||||
| 235 | int get_group_creds(const char **groupname, gid_t *gid) { | |||
| 236 | struct group *g; | |||
| 237 | gid_t id; | |||
| 238 | ||||
| 239 | assert(groupname)do { if ((__builtin_expect(!!(!(groupname)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("groupname"), "../src/basic/user-util.c" , 239, __PRETTY_FUNCTION__); } while (0); | |||
| 240 | ||||
| 241 | /* We enforce some special rules for gid=0: in order to avoid | |||
| 242 | * NSS lookups for root we hardcode its data. */ | |||
| 243 | ||||
| 244 | if (STR_IN_SET(*groupname, "root", "0")(!!strv_find((((char**) ((const char*[]) { "root", "0", ((void *)0) }))), (*groupname)))) { | |||
| 245 | *groupname = "root"; | |||
| 246 | ||||
| 247 | if (gid) | |||
| 248 | *gid = 0; | |||
| 249 | ||||
| 250 | return 0; | |||
| 251 | } | |||
| 252 | ||||
| 253 | if (synthesize_nobody() && | |||
| 254 | STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")(!!strv_find((((char**) ((const char*[]) { "nobody", "65534", ((void*)0) }))), (*groupname)))) { | |||
| 255 | *groupname = NOBODY_GROUP_NAME"nobody"; | |||
| 256 | ||||
| 257 | if (gid) | |||
| 258 | *gid = GID_NOBODY((gid_t) 65534U); | |||
| 259 | ||||
| 260 | return 0; | |||
| 261 | } | |||
| 262 | ||||
| 263 | if (parse_gid(*groupname, &id) >= 0) { | |||
| 264 | errno(*__errno_location ()) = 0; | |||
| 265 | g = getgrgid(id); | |||
| 266 | ||||
| 267 | if (g) | |||
| 268 | *groupname = g->gr_name; | |||
| 269 | } else { | |||
| 270 | errno(*__errno_location ()) = 0; | |||
| 271 | g = getgrnam(*groupname); | |||
| 272 | } | |||
| 273 | ||||
| 274 | if (!g) | |||
| 275 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
| 276 | ||||
| 277 | if (gid) { | |||
| 278 | if (!gid_is_valid(g->gr_gid)) | |||
| 279 | return -EBADMSG74; | |||
| 280 | ||||
| 281 | *gid = g->gr_gid; | |||
| 282 | } | |||
| 283 | ||||
| 284 | return 0; | |||
| 285 | } | |||
| 286 | ||||
| 287 | char* uid_to_name(uid_t uid) { | |||
| 288 | char *ret; | |||
| 289 | int r; | |||
| 290 | ||||
| 291 | /* Shortcut things to avoid NSS lookups */ | |||
| 292 | if (uid == 0) | |||
| 293 | return strdup("root"); | |||
| 294 | if (synthesize_nobody() && | |||
| 295 | uid == UID_NOBODY((uid_t) 65534U)) | |||
| 296 | return strdup(NOBODY_USER_NAME"nobody"); | |||
| 297 | ||||
| 298 | if (uid_is_valid(uid)) { | |||
| 299 | long bufsize; | |||
| 300 | ||||
| 301 | bufsize = sysconf(_SC_GETPW_R_SIZE_MAX_SC_GETPW_R_SIZE_MAX); | |||
| 302 | if (bufsize <= 0) | |||
| 303 | bufsize = 4096; | |||
| 304 | ||||
| 305 | for (;;) { | |||
| 306 | struct passwd pwbuf, *pw = NULL((void*)0); | |||
| 307 | _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0); | |||
| 308 | ||||
| 309 | buf = malloc(bufsize); | |||
| 310 | if (!buf) | |||
| 311 | return NULL((void*)0); | |||
| 312 | ||||
| 313 | r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw); | |||
| 314 | if (r == 0 && pw) | |||
| 315 | return strdup(pw->pw_name); | |||
| 316 | if (r != ERANGE34) | |||
| 317 | break; | |||
| 318 | ||||
| 319 | bufsize *= 2; | |||
| 320 | } | |||
| 321 | } | |||
| 322 | ||||
| 323 | if (asprintf(&ret, UID_FMT"%" "u", uid) < 0) | |||
| 324 | return NULL((void*)0); | |||
| 325 | ||||
| 326 | return ret; | |||
| 327 | } | |||
| 328 | ||||
| 329 | char* gid_to_name(gid_t gid) { | |||
| 330 | char *ret; | |||
| 331 | int r; | |||
| 332 | ||||
| 333 | if (gid == 0) | |||
| 334 | return strdup("root"); | |||
| 335 | if (synthesize_nobody() && | |||
| 336 | gid == GID_NOBODY((gid_t) 65534U)) | |||
| 337 | return strdup(NOBODY_GROUP_NAME"nobody"); | |||
| 338 | ||||
| 339 | if (gid_is_valid(gid)) { | |||
| 340 | long bufsize; | |||
| 341 | ||||
| 342 | bufsize = sysconf(_SC_GETGR_R_SIZE_MAX_SC_GETGR_R_SIZE_MAX); | |||
| 343 | if (bufsize <= 0) | |||
| 344 | bufsize = 4096; | |||
| 345 | ||||
| 346 | for (;;) { | |||
| 347 | struct group grbuf, *gr = NULL((void*)0); | |||
| 348 | _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0); | |||
| 349 | ||||
| 350 | buf = malloc(bufsize); | |||
| 351 | if (!buf) | |||
| 352 | return NULL((void*)0); | |||
| 353 | ||||
| 354 | r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr); | |||
| 355 | if (r == 0 && gr) | |||
| 356 | return strdup(gr->gr_name); | |||
| 357 | if (r != ERANGE34) | |||
| 358 | break; | |||
| 359 | ||||
| 360 | bufsize *= 2; | |||
| 361 | } | |||
| 362 | } | |||
| 363 | ||||
| 364 | if (asprintf(&ret, GID_FMT"%" "u", gid) < 0) | |||
| 365 | return NULL((void*)0); | |||
| 366 | ||||
| 367 | return ret; | |||
| 368 | } | |||
| 369 | ||||
| 370 | int in_gid(gid_t gid) { | |||
| 371 | gid_t *gids; | |||
| 372 | int ngroups, r, i; | |||
| 373 | ||||
| 374 | if (getgid() == gid) | |||
| 375 | return 1; | |||
| 376 | ||||
| 377 | if (getegid() == gid) | |||
| 378 | return 1; | |||
| 379 | ||||
| 380 | if (!gid_is_valid(gid)) | |||
| 381 | return -EINVAL22; | |||
| 382 | ||||
| 383 | ngroups = getgroups(0, NULL((void*)0)); | |||
| 384 | if (ngroups < 0) | |||
| 385 | return -errno(*__errno_location ()); | |||
| 386 | if (ngroups == 0) | |||
| 387 | return 0; | |||
| 388 | ||||
| 389 | gids = newa(gid_t, ngroups)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof (gid_t), ngroups))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("!size_multiply_overflow(sizeof(gid_t), ngroups)"), "../src/basic/user-util.c" , 389, __PRETTY_FUNCTION__); } while (0); (gid_t*) __builtin_alloca (sizeof(gid_t)*(ngroups)); }); | |||
| 390 | ||||
| 391 | r = getgroups(ngroups, gids); | |||
| 392 | if (r < 0) | |||
| 393 | return -errno(*__errno_location ()); | |||
| 394 | ||||
| 395 | for (i = 0; i < r; i++) | |||
| 396 | if (gids[i] == gid) | |||
| 397 | return 1; | |||
| 398 | ||||
| 399 | return 0; | |||
| 400 | } | |||
| 401 | ||||
| 402 | int in_group(const char *name) { | |||
| 403 | int r; | |||
| 404 | gid_t gid; | |||
| ||||
| 405 | ||||
| 406 | r = get_group_creds(&name, &gid); | |||
| 407 | if (r < 0) | |||
| 408 | return r; | |||
| 409 | ||||
| 410 | return in_gid(gid); | |||
| ||||
| 411 | } | |||
| 412 | ||||
| 413 | int get_home_dir(char **_h) { | |||
| 414 | struct passwd *p; | |||
| 415 | const char *e; | |||
| 416 | char *h; | |||
| 417 | uid_t u; | |||
| 418 | ||||
| 419 | assert(_h)do { if ((__builtin_expect(!!(!(_h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_h"), "../src/basic/user-util.c", 419, __PRETTY_FUNCTION__ ); } while (0); | |||
| 420 | ||||
| 421 | /* Take the user specified one */ | |||
| 422 | e = secure_getenv("HOME"); | |||
| 423 | if (e && path_is_absolute(e)) { | |||
| 424 | h = strdup(e); | |||
| 425 | if (!h) | |||
| 426 | return -ENOMEM12; | |||
| 427 | ||||
| 428 | *_h = h; | |||
| 429 | return 0; | |||
| 430 | } | |||
| 431 | ||||
| 432 | /* Hardcode home directory for root and nobody to avoid NSS */ | |||
| 433 | u = getuid(); | |||
| 434 | if (u == 0) { | |||
| 435 | h = strdup("/root"); | |||
| 436 | if (!h) | |||
| 437 | return -ENOMEM12; | |||
| 438 | ||||
| 439 | *_h = h; | |||
| 440 | return 0; | |||
| 441 | } | |||
| 442 | if (synthesize_nobody() && | |||
| 443 | u == UID_NOBODY((uid_t) 65534U)) { | |||
| 444 | h = strdup("/"); | |||
| 445 | if (!h) | |||
| 446 | return -ENOMEM12; | |||
| 447 | ||||
| 448 | *_h = h; | |||
| 449 | return 0; | |||
| 450 | } | |||
| 451 | ||||
| 452 | /* Check the database... */ | |||
| 453 | errno(*__errno_location ()) = 0; | |||
| 454 | p = getpwuid(u); | |||
| 455 | if (!p) | |||
| 456 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
| 457 | ||||
| 458 | if (!path_is_absolute(p->pw_dir)) | |||
| 459 | return -EINVAL22; | |||
| 460 | ||||
| 461 | h = strdup(p->pw_dir); | |||
| 462 | if (!h) | |||
| 463 | return -ENOMEM12; | |||
| 464 | ||||
| 465 | *_h = h; | |||
| 466 | return 0; | |||
| 467 | } | |||
| 468 | ||||
| 469 | int get_shell(char **_s) { | |||
| 470 | struct passwd *p; | |||
| 471 | const char *e; | |||
| 472 | char *s; | |||
| 473 | uid_t u; | |||
| 474 | ||||
| 475 | assert(_s)do { if ((__builtin_expect(!!(!(_s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_s"), "../src/basic/user-util.c", 475, __PRETTY_FUNCTION__ ); } while (0); | |||
| 476 | ||||
| 477 | /* Take the user specified one */ | |||
| 478 | e = getenv("SHELL"); | |||
| 479 | if (e) { | |||
| 480 | s = strdup(e); | |||
| 481 | if (!s) | |||
| 482 | return -ENOMEM12; | |||
| 483 | ||||
| 484 | *_s = s; | |||
| 485 | return 0; | |||
| 486 | } | |||
| 487 | ||||
| 488 | /* Hardcode shell for root and nobody to avoid NSS */ | |||
| 489 | u = getuid(); | |||
| 490 | if (u == 0) { | |||
| 491 | s = strdup("/bin/sh"); | |||
| 492 | if (!s) | |||
| 493 | return -ENOMEM12; | |||
| 494 | ||||
| 495 | *_s = s; | |||
| 496 | return 0; | |||
| 497 | } | |||
| 498 | if (synthesize_nobody() && | |||
| 499 | u == UID_NOBODY((uid_t) 65534U)) { | |||
| 500 | s = strdup("/sbin/nologin"); | |||
| 501 | if (!s) | |||
| 502 | return -ENOMEM12; | |||
| 503 | ||||
| 504 | *_s = s; | |||
| 505 | return 0; | |||
| 506 | } | |||
| 507 | ||||
| 508 | /* Check the database... */ | |||
| 509 | errno(*__errno_location ()) = 0; | |||
| 510 | p = getpwuid(u); | |||
| 511 | if (!p) | |||
| 512 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
| 513 | ||||
| 514 | if (!path_is_absolute(p->pw_shell)) | |||
| 515 | return -EINVAL22; | |||
| 516 | ||||
| 517 | s = strdup(p->pw_shell); | |||
| 518 | if (!s) | |||
| 519 | return -ENOMEM12; | |||
| 520 | ||||
| 521 | *_s = s; | |||
| 522 | return 0; | |||
| 523 | } | |||
| 524 | ||||
| 525 | int reset_uid_gid(void) { | |||
| 526 | int r; | |||
| 527 | ||||
| 528 | r = maybe_setgroups(0, NULL((void*)0)); | |||
| 529 | if (r < 0) | |||
| 530 | return r; | |||
| 531 | ||||
| 532 | if (setresgid(0, 0, 0) < 0) | |||
| 533 | return -errno(*__errno_location ()); | |||
| 534 | ||||
| 535 | if (setresuid(0, 0, 0) < 0) | |||
| 536 | return -errno(*__errno_location ()); | |||
| 537 | ||||
| 538 | return 0; | |||
| 539 | } | |||
| 540 | ||||
| 541 | int take_etc_passwd_lock(const char *root) { | |||
| 542 | ||||
| 543 | struct flock flock = { | |||
| 544 | .l_type = F_WRLCK1, | |||
| 545 | .l_whence = SEEK_SET0, | |||
| 546 | .l_start = 0, | |||
| 547 | .l_len = 0, | |||
| 548 | }; | |||
| 549 | ||||
| 550 | const char *path; | |||
| 551 | int fd, r; | |||
| 552 | ||||
| 553 | /* This is roughly the same as lckpwdf(), but not as awful. We | |||
| 554 | * don't want to use alarm() and signals, hence we implement | |||
| 555 | * our own trivial version of this. | |||
| 556 | * | |||
| 557 | * Note that shadow-utils also takes per-database locks in | |||
| 558 | * addition to lckpwdf(). However, we don't given that they | |||
| 559 | * are redundant as they invoke lckpwdf() first and keep | |||
| 560 | * it during everything they do. The per-database locks are | |||
| 561 | * awfully racy, and thus we just won't do them. */ | |||
| 562 | ||||
| 563 | if (root) | |||
| 564 | path = prefix_roota(root, ETC_PASSWD_LOCK_PATH)({ const char* _path = ("/etc/.pwd.lock"), *_root = (root), * _ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path[1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path ; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 565 | else | |||
| 566 | path = ETC_PASSWD_LOCK_PATH"/etc/.pwd.lock"; | |||
| 567 | ||||
| 568 | fd = open(path, O_WRONLY01|O_CREAT0100|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000, 0600); | |||
| 569 | if (fd < 0) | |||
| 570 | return log_debug_errno(errno, "Cannot open %s: %m", path)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/basic/user-util.c", 570, __func__ , "Cannot open %s: %m", path) : -abs(_e); }); | |||
| 571 | ||||
| 572 | r = fcntl(fd, F_SETLKW7, &flock); | |||
| 573 | if (r < 0) { | |||
| 574 | safe_close(fd); | |||
| 575 | return log_debug_errno(errno, "Locking %s failed: %m", path)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/basic/user-util.c", 575, __func__ , "Locking %s failed: %m", path) : -abs(_e); }); | |||
| 576 | } | |||
| 577 | ||||
| 578 | return fd; | |||
| 579 | } | |||
| 580 | ||||
| 581 | bool_Bool valid_user_group_name(const char *u, ValidUserFlags flags) { | |||
| 582 | const char *i; | |||
| 583 | ||||
| 584 | /* Checks if the specified name is a valid user/group name. There are two flavours of this call: | |||
| 585 | * strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept | |||
| 586 | * pretty much everything except the really worst offending names. | |||
| 587 | * | |||
| 588 | * Whenever we synthesize users ourselves we should use the strict mode. But when we process users | |||
| 589 | * created by other stuff, let's be more liberal. */ | |||
| 590 | ||||
| 591 | if (isempty(u)) /* An empty user name is never valid */ | |||
| 592 | return false0; | |||
| 593 | ||||
| 594 | if (parse_uid(u, NULL((void*)0)) >= 0) /* Something that parses as numeric UID string is valid exactly when the | |||
| 595 | * flag for it is set */ | |||
| 596 | return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC)(((flags) & (VALID_USER_ALLOW_NUMERIC)) == (VALID_USER_ALLOW_NUMERIC )); | |||
| 597 | ||||
| 598 | if (FLAGS_SET(flags, VALID_USER_RELAX)(((flags) & (VALID_USER_RELAX)) == (VALID_USER_RELAX))) { | |||
| 599 | ||||
| 600 | /* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is | |||
| 601 | * extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which | |||
| 602 | * is bound to cause problems for example when used with an MTA), hence only filter the most | |||
| 603 | * obvious cases, or where things would result in an invalid entry if such a user name would | |||
| 604 | * show up in /etc/passwd (or equivalent getent output). | |||
| 605 | * | |||
| 606 | * Note that we stepped far out of POSIX territory here. It's not our fault though, but | |||
| 607 | * SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step | |||
| 608 | * outside of POSIX' bounds any day, but I must say in this case I probably wouldn't | |||
| 609 | * have...) */ | |||
| 610 | ||||
| 611 | if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed | |||
| 612 | * at front and back (accept in the middle, since | |||
| 613 | * that's apparently a thing on Windows). Note | |||
| 614 | * that this also blocks usernames consisting of | |||
| 615 | * whitespace only. */ | |||
| 616 | return false0; | |||
| 617 | ||||
| 618 | if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */ | |||
| 619 | return false0; | |||
| 620 | ||||
| 621 | if (string_has_cc(u, NULL((void*)0))) /* CC characters are just dangerous (and \n in particular is the | |||
| 622 | * record separator in /etc/passwd), so we can't allow that. */ | |||
| 623 | return false0; | |||
| 624 | ||||
| 625 | if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow | |||
| 626 | * that. Slashes are special to file systems paths and user names | |||
| 627 | * typically show up in the file system as home directories, hence | |||
| 628 | * don't allow slashes. */ | |||
| 629 | return false0; | |||
| 630 | ||||
| 631 | if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused | |||
| 632 | * with with UIDs (note that this test is more broad than | |||
| 633 | * the parse_uid() test above, as it will cover more than | |||
| 634 | * the 32bit range, and it will detect 65535 (which is in | |||
| 635 | * invalid UID, even though in the unsigned 32 bit range) */ | |||
| 636 | return false0; | |||
| 637 | ||||
| 638 | if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric | |||
| 639 | * strings either. After all some people | |||
| 640 | * write 65535 as -1 (even though that's | |||
| 641 | * not even true on 32bit uid_t | |||
| 642 | * anyway) */ | |||
| 643 | return false0; | |||
| 644 | ||||
| 645 | if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are | |||
| 646 | * special in that context, don't allow that. */ | |||
| 647 | return false0; | |||
| 648 | ||||
| 649 | /* Compare with strict result and warn if result doesn't match */ | |||
| 650 | if (FLAGS_SET(flags, VALID_USER_WARN)(((flags) & (VALID_USER_WARN)) == (VALID_USER_WARN)) && !valid_user_group_name(u, 0)) | |||
| 651 | log_struct(LOG_NOTICE,log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)) | |||
| 652 | "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules.", u,log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)) | |||
| 653 | "USER_GROUP_NAME=%s", u,log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)) | |||
| 654 | "MESSAGE_ID=" SD_MESSAGE_UNSAFE_USER_NAME_STR)log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)); | |||
| 655 | ||||
| 656 | /* Note that we make no restrictions on the length in relaxed mode! */ | |||
| 657 | } else { | |||
| 658 | long sz; | |||
| 659 | size_t l; | |||
| 660 | ||||
| 661 | /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here | |||
| 662 | * however. Specifically we deviate from POSIX rules: | |||
| 663 | * | |||
| 664 | * - We don't allow empty user names (see above) | |||
| 665 | * - We require that names fit into the appropriate utmp field | |||
| 666 | * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) | |||
| 667 | * - We don't allow dashes or digit as the first character | |||
| 668 | * | |||
| 669 | * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. | |||
| 670 | */ | |||
| 671 | ||||
| 672 | if (!(u[0] >= 'a' && u[0] <= 'z') && | |||
| 673 | !(u[0] >= 'A' && u[0] <= 'Z') && | |||
| 674 | u[0] != '_') | |||
| 675 | return false0; | |||
| 676 | ||||
| 677 | for (i = u+1; *i; i++) | |||
| 678 | if (!(*i >= 'a' && *i <= 'z') && | |||
| 679 | !(*i >= 'A' && *i <= 'Z') && | |||
| 680 | !(*i >= '0' && *i <= '9') && | |||
| 681 | !IN_SET(*i, '_', '-')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){'_', '-'})/sizeof(int)]; switch(*i) { case '_': case '-': _found = 1; break; default: break; } _found; } )) | |||
| 682 | return false0; | |||
| 683 | ||||
| 684 | l = i - u; | |||
| 685 | ||||
| 686 | sz = sysconf(_SC_LOGIN_NAME_MAX_SC_LOGIN_NAME_MAX); | |||
| 687 | assert_se(sz > 0)do { if ((__builtin_expect(!!(!(sz > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sz > 0"), "../src/basic/user-util.c" , 687, __PRETTY_FUNCTION__); } while (0); | |||
| 688 | ||||
| 689 | if (l > (size_t) sz) | |||
| 690 | return false0; | |||
| 691 | if (l > FILENAME_MAX4096) | |||
| 692 | return false0; | |||
| 693 | if (l > UT_NAMESIZE32 - 1) | |||
| 694 | return false0; | |||
| 695 | } | |||
| 696 | ||||
| 697 | return true1; | |||
| 698 | } | |||
| 699 | ||||
| 700 | bool_Bool valid_gecos(const char *d) { | |||
| 701 | ||||
| 702 | if (!d) | |||
| 703 | return false0; | |||
| 704 | ||||
| 705 | if (!utf8_is_valid(d)) | |||
| 706 | return false0; | |||
| 707 | ||||
| 708 | if (string_has_cc(d, NULL((void*)0))) | |||
| 709 | return false0; | |||
| 710 | ||||
| 711 | /* Colons are used as field separators, and hence not OK */ | |||
| 712 | if (strchr(d, ':')) | |||
| 713 | return false0; | |||
| 714 | ||||
| 715 | return true1; | |||
| 716 | } | |||
| 717 | ||||
| 718 | bool_Bool valid_home(const char *p) { | |||
| 719 | /* Note that this function is also called by valid_shell(), any | |||
| 720 | * changes must account for that. */ | |||
| 721 | ||||
| 722 | if (isempty(p)) | |||
| 723 | return false0; | |||
| 724 | ||||
| 725 | if (!utf8_is_valid(p)) | |||
| 726 | return false0; | |||
| 727 | ||||
| 728 | if (string_has_cc(p, NULL((void*)0))) | |||
| 729 | return false0; | |||
| 730 | ||||
| 731 | if (!path_is_absolute(p)) | |||
| 732 | return false0; | |||
| 733 | ||||
| 734 | if (!path_is_normalized(p)) | |||
| 735 | return false0; | |||
| 736 | ||||
| 737 | /* Colons are used as field separators, and hence not OK */ | |||
| 738 | if (strchr(p, ':')) | |||
| 739 | return false0; | |||
| 740 | ||||
| 741 | return true1; | |||
| 742 | } | |||
| 743 | ||||
| 744 | int maybe_setgroups(size_t size, const gid_t *list) { | |||
| 745 | int r; | |||
| 746 | ||||
| 747 | /* Check if setgroups is allowed before we try to drop all the auxiliary groups */ | |||
| 748 | if (size == 0) { /* Dropping all aux groups? */ | |||
| 749 | _cleanup_free___attribute__((cleanup(freep))) char *setgroups_content = NULL((void*)0); | |||
| 750 | bool_Bool can_setgroups; | |||
| 751 | ||||
| 752 | r = read_one_line_file("/proc/self/setgroups", &setgroups_content); | |||
| 753 | if (r == -ENOENT2) | |||
| 754 | /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */ | |||
| 755 | can_setgroups = true1; | |||
| 756 | else if (r < 0) | |||
| 757 | return r; | |||
| 758 | else | |||
| 759 | can_setgroups = streq(setgroups_content, "allow")(strcmp((setgroups_content),("allow")) == 0); | |||
| 760 | ||||
| 761 | if (!can_setgroups) { | |||
| 762 | log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/user-util.c", 762, __func__, "Skipping setgroups(), /proc/self/setgroups is set to 'deny'" ) : -abs(_e); }); | |||
| 763 | return 0; | |||
| 764 | } | |||
| 765 | } | |||
| 766 | ||||
| 767 | if (setgroups(size, list) < 0) | |||
| 768 | return -errno(*__errno_location ()); | |||
| 769 | ||||
| 770 | return 0; | |||
| 771 | } | |||
| 772 | ||||
| 773 | bool_Bool synthesize_nobody(void) { | |||
| 774 | ||||
| 775 | #ifdef NOLEGACY | |||
| 776 | return true1; | |||
| 777 | #else | |||
| 778 | /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by | |||
| 779 | * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems | |||
| 780 | * that used the "nobody" user name and group name for other UIDs/GIDs than 65534. | |||
| 781 | * | |||
| 782 | * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is | |||
| 783 | * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that | |||
| 784 | * shouldn't matter as each initialization should come to the same result. */ | |||
| 785 | static int cache = -1; | |||
| 786 | ||||
| 787 | if (cache < 0) | |||
| 788 | cache = access("/etc/systemd/dont-synthesize-nobody", F_OK0) < 0; | |||
| 789 | ||||
| 790 | return cache; | |||
| 791 | #endif | |||
| 792 | } | |||
| 793 | ||||
| 794 | int putpwent_sane(const struct passwd *pw, FILE *stream) { | |||
| 795 | assert(pw)do { if ((__builtin_expect(!!(!(pw)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pw"), "../src/basic/user-util.c", 795, __PRETTY_FUNCTION__ ); } while (0); | |||
| 796 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 796 , __PRETTY_FUNCTION__); } while (0); | |||
| 797 | ||||
| 798 | errno(*__errno_location ()) = 0; | |||
| 799 | if (putpwent(pw, stream) != 0) | |||
| 800 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 801 | ||||
| 802 | return 0; | |||
| 803 | } | |||
| 804 | ||||
| 805 | int putspent_sane(const struct spwd *sp, FILE *stream) { | |||
| 806 | assert(sp)do { if ((__builtin_expect(!!(!(sp)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sp"), "../src/basic/user-util.c", 806, __PRETTY_FUNCTION__ ); } while (0); | |||
| 807 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 807 , __PRETTY_FUNCTION__); } while (0); | |||
| 808 | ||||
| 809 | errno(*__errno_location ()) = 0; | |||
| 810 | if (putspent(sp, stream) != 0) | |||
| 811 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 812 | ||||
| 813 | return 0; | |||
| 814 | } | |||
| 815 | ||||
| 816 | int putgrent_sane(const struct group *gr, FILE *stream) { | |||
| 817 | assert(gr)do { if ((__builtin_expect(!!(!(gr)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("gr"), "../src/basic/user-util.c", 817, __PRETTY_FUNCTION__ ); } while (0); | |||
| 818 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 818 , __PRETTY_FUNCTION__); } while (0); | |||
| 819 | ||||
| 820 | errno(*__errno_location ()) = 0; | |||
| 821 | if (putgrent(gr, stream) != 0) | |||
| 822 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 823 | ||||
| 824 | return 0; | |||
| 825 | } | |||
| 826 | ||||
| 827 | #if ENABLE_GSHADOW1 | |||
| 828 | int putsgent_sane(const struct sgrp *sg, FILE *stream) { | |||
| 829 | assert(sg)do { if ((__builtin_expect(!!(!(sg)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sg"), "../src/basic/user-util.c", 829, __PRETTY_FUNCTION__ ); } while (0); | |||
| 830 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 830 , __PRETTY_FUNCTION__); } while (0); | |||
| 831 | ||||
| 832 | errno(*__errno_location ()) = 0; | |||
| 833 | if (putsgent(sg, stream) != 0) | |||
| 834 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 835 | ||||
| 836 | return 0; | |||
| 837 | } | |||
| 838 | #endif | |||
| 839 | ||||
| 840 | int fgetpwent_sane(FILE *stream, struct passwd **pw) { | |||
| 841 | struct passwd *p; | |||
| 842 | ||||
| 843 | assert(pw)do { if ((__builtin_expect(!!(!(pw)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pw"), "../src/basic/user-util.c", 843, __PRETTY_FUNCTION__ ); } while (0); | |||
| 844 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 844 , __PRETTY_FUNCTION__); } while (0); | |||
| 845 | ||||
| 846 | errno(*__errno_location ()) = 0; | |||
| 847 | p = fgetpwent(stream); | |||
| 848 | if (!p && errno(*__errno_location ()) != ENOENT2) | |||
| 849 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 850 | ||||
| 851 | *pw = p; | |||
| 852 | return !!p; | |||
| 853 | } | |||
| 854 | ||||
| 855 | int fgetspent_sane(FILE *stream, struct spwd **sp) { | |||
| 856 | struct spwd *s; | |||
| 857 | ||||
| 858 | assert(sp)do { if ((__builtin_expect(!!(!(sp)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sp"), "../src/basic/user-util.c", 858, __PRETTY_FUNCTION__ ); } while (0); | |||
| 859 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 859 , __PRETTY_FUNCTION__); } while (0); | |||
| 860 | ||||
| 861 | errno(*__errno_location ()) = 0; | |||
| 862 | s = fgetspent(stream); | |||
| 863 | if (!s && errno(*__errno_location ()) != ENOENT2) | |||
| 864 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 865 | ||||
| 866 | *sp = s; | |||
| 867 | return !!s; | |||
| 868 | } | |||
| 869 | ||||
| 870 | int fgetgrent_sane(FILE *stream, struct group **gr) { | |||
| 871 | struct group *g; | |||
| 872 | ||||
| 873 | assert(gr)do { if ((__builtin_expect(!!(!(gr)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("gr"), "../src/basic/user-util.c", 873, __PRETTY_FUNCTION__ ); } while (0); | |||
| 874 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 874 , __PRETTY_FUNCTION__); } while (0); | |||
| 875 | ||||
| 876 | errno(*__errno_location ()) = 0; | |||
| 877 | g = fgetgrent(stream); | |||
| 878 | if (!g && errno(*__errno_location ()) != ENOENT2) | |||
| 879 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 880 | ||||
| 881 | *gr = g; | |||
| 882 | return !!g; | |||
| 883 | } | |||
| 884 | ||||
| 885 | #if ENABLE_GSHADOW1 | |||
| 886 | int fgetsgent_sane(FILE *stream, struct sgrp **sg) { | |||
| 887 | struct sgrp *s; | |||
| 888 | ||||
| 889 | assert(sg)do { if ((__builtin_expect(!!(!(sg)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sg"), "../src/basic/user-util.c", 889, __PRETTY_FUNCTION__ ); } while (0); | |||
| 890 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 890 , __PRETTY_FUNCTION__); } while (0); | |||
| 891 | ||||
| 892 | errno(*__errno_location ()) = 0; | |||
| 893 | s = fgetsgent(stream); | |||
| 894 | if (!s && errno(*__errno_location ()) != ENOENT2) | |||
| 895 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
| 896 | ||||
| 897 | *sg = s; | |||
| 898 | return !!s; | |||
| 899 | } | |||
| 900 | #endif |