diff --git a/configure.ac b/configure.ac index 3591abce52..f4cde4fab3 100644 --- a/configure.ac +++ b/configure.ac @@ -65,7 +65,7 @@ AC_CHECK_MEMBERS([struct utmpx.ut_name, dnl Checks for library functions. AC_FUNC_UTIME_NULL AC_REPLACE_FUNCS([putgrent putpwent putspent]) -AC_REPLACE_FUNCS([sgetgrent sgetpwent sgetspent]) +AC_REPLACE_FUNCS([sgetgrent sgetpwent sgetspent sgetspent_r]) AC_CHECK_FUNC([setpgrp]) AC_CHECK_FUNC([secure_getenv], diff --git a/lib/Makefile.am b/lib/Makefile.am index c402ff02a3..ffad2b4da7 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -106,6 +106,7 @@ libshadow_la_SOURCES = \ isexpired.c \ limits.c \ list.c \ + list.h \ lockpw.c \ loginprompt.c \ mail.c \ @@ -175,6 +176,8 @@ libshadow_la_SOURCES = \ shadow/passwd/sgetpwent.h \ shadow/shadow/sgetspent.c \ shadow/shadow/sgetspent.h \ + shadow/subid/sgetsient.c \ + shadow/subid/sgetsient.h \ shadowio.c \ shadowio.h \ shadowlog.c \ diff --git a/lib/addgrps.c b/lib/addgrps.c index 6ab34dcb31..ecab30a740 100644 --- a/lib/addgrps.c +++ b/lib/addgrps.c @@ -77,6 +77,4 @@ add_groups(const char *list) free(gids); return -1; } -#else /* !USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !USE_PAM */ diff --git a/lib/age.c b/lib/age.c index bdb789e30d..c988e23542 100644 --- a/lib/age.c +++ b/lib/age.c @@ -125,7 +125,8 @@ int expire (const struct passwd *pw, /*@null@*/const struct spwd *sp) exit (EXIT_FAILURE); } - while (((child = wait (&status)) != pid) && (child != (pid_t)-1)); + while (((child = wait (&status)) != pid) && (child != (pid_t)-1)) + continue; if ((child == pid) && (0 == status)) { return 1; diff --git a/lib/audit_help.c b/lib/audit_help.c index b0ac5631e5..d93d166bb1 100644 --- a/lib/audit_help.c +++ b/lib/audit_help.c @@ -128,8 +128,4 @@ void audit_logger_message (const char *message, shadow_audit_result result) result); } } - -#else /* WITH_AUDIT */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* WITH_AUDIT */ - diff --git a/lib/cleanup.c b/lib/cleanup.c index 0b332cdb95..89b0d617fc 100644 --- a/lib/cleanup.c +++ b/lib/cleanup.c @@ -75,7 +75,8 @@ void add_cleanup (/*@notnull@*/cleanup_function pcf, /*@null@*/void *arg) } /* Add the cleanup_function at the end of the stack */ - for (i=0; NULL != cleanup_functions[i]; i++); + for (i=0; NULL != cleanup_functions[i]; i++) + continue; cleanup_functions[i] = pcf; cleanup_function_args[i] = arg; } diff --git a/lib/find_new_sub_gids.c b/lib/find_new_sub_gids.c index ee85c4f354..4de1327acc 100644 --- a/lib/find_new_sub_gids.c +++ b/lib/find_new_sub_gids.c @@ -58,7 +58,4 @@ int find_new_sub_gids (gid_t *range_start, unsigned long *range_count) *range_count = count; return 0; } -#else /* !ENABLE_SUBIDS */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !ENABLE_SUBIDS */ - diff --git a/lib/find_new_sub_uids.c b/lib/find_new_sub_uids.c index 52dbee3831..969eec0799 100644 --- a/lib/find_new_sub_uids.c +++ b/lib/find_new_sub_uids.c @@ -58,7 +58,4 @@ int find_new_sub_uids (uid_t *range_start, unsigned long *range_count) *range_count = count; return 0; } -#else /* !ENABLE_SUBIDS */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !ENABLE_SUBIDS */ - diff --git a/lib/groupio.c b/lib/groupio.c index f62a9bbd97..67035c9ce2 100644 --- a/lib/groupio.c +++ b/lib/groupio.c @@ -330,7 +330,8 @@ static /*@null@*/struct commonio_entry *merge_group_entries ( return NULL; /* Concatenate the 2 list of members */ - for (i=0; NULL != gptr1->gr_mem[i]; i++); + for (i=0; NULL != gptr1->gr_mem[i]; i++) + continue; members += i; for (i=0; NULL != gptr2->gr_mem[i]; i++) { char **pmember = gptr1->gr_mem; @@ -400,7 +401,8 @@ static int split_groups (unsigned int max_members) if (NULL == gptr) { continue; } - for (members = 0; NULL != gptr->gr_mem[members]; members++); + for (members = 0; NULL != gptr->gr_mem[members]; members++) + continue; if (members <= max_members) { continue; } diff --git a/lib/groupmem.c b/lib/groupmem.c index 1ebe2f9ba0..19efa11775 100644 --- a/lib/groupmem.c +++ b/lib/groupmem.c @@ -46,7 +46,8 @@ return NULL; } - for (i = 0; grent->gr_mem[i]; i++); + for (i = 0; grent->gr_mem[i]; i++) + continue; /*@-mustfreeonly@*/ gr->gr_mem = malloc_T(i + 1, char *); diff --git a/lib/limits.c b/lib/limits.c index 2d80a3217a..c919aee6c1 100644 --- a/lib/limits.c +++ b/lib/limits.c @@ -17,20 +17,19 @@ #ifndef USE_PAM -#ident "$Id$" - -#include -#include -#include #include -#include "prototypes.h" -#include "defines.h" #include -#include "getdef.h" -#include "shadowlog.h" +#include +#include +#include #include #include "atoi/a2i.h" +#include "defines.h" +#include "list.h" +#include "getdef.h" +#include "prototypes.h" +#include "shadowlog.h" #include "string/memset/memzero.h" #include "string/strcmp/streq.h" #include "string/strcmp/strprefix.h" @@ -524,8 +523,4 @@ void setup_limits (const struct passwd *info) } } } - -#else /* !USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !USE_PAM */ - diff --git a/lib/list.c b/lib/list.c index cf918f477f..bba309b44e 100644 --- a/lib/list.c +++ b/lib/list.c @@ -8,16 +8,19 @@ #include "config.h" -#ident "$Id$" +#include "list.h" + +#include +#include +#include #include #include "alloc/malloc.h" -#include "prototypes.h" #include "defines.h" -#include "string/strchr/strchrcnt.h" #include "string/strcmp/streq.h" #include "string/strdup/strdup.h" +#include "string/strtok/astrsep2ls.h" #include "string/strtok/strsep2ls.h" @@ -142,7 +145,8 @@ dup_list(char *const *list) assert (NULL != list); - for (i = 0; NULL != list[i]; i++); + for (i = 0; NULL != list[i]; i++) + continue; tmp = xmalloc_T(i + 1, char *); @@ -198,7 +202,6 @@ comma_to_list(const char *comma) { char *members; char **array; - size_t n; assert (NULL != comma); @@ -208,26 +211,45 @@ comma_to_list(const char *comma) members = xstrdup (comma); - /* - * Allocate the array we're going to store the pointers into. - * n: number of delimiters + last element + NULL - */ + array = acsv2ls(members); + if (array == NULL) + exit(EXIT_FAILURE); - n = strchrcnt(members, ',') + 2; - array = xmalloc_T(n, char *); + if (array[0] == NULL) + free(members); - /* - * Empty list is special - 0 members, not 1 empty member. --marekm - */ + return array; +} - if (streq(members, "")) { - *array = NULL; - free (members); - return array; - } - strsep2ls(members, ",", n, array); +char ** +acsv2ls(char *s) +{ + char **l; + size_t n; + + l = astrsep2ls(s, ",", &n); + if (l == NULL) + return NULL; - return array; + if (streq(l[n-1], "")) + l[n-1] = NULL; + + return l; } + +int +csv2ls(char *s, size_t n, char *ls[restrict n]) +{ + ssize_t i; + + i = strsep2ls(s, ",", n, ls); + if (i == -1) + return -1; + + if (streq(ls[i-1], "")) + ls[i-1] = NULL; + + return 0; +} diff --git a/lib/list.h b/lib/list.h new file mode 100644 index 0000000000..eb346dd27d --- /dev/null +++ b/lib/list.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2025, Alejandro Colomar +// SPDX-License-Identifier: BSD-3-Clause + + +#ifndef SHADOW_INCLUDE_LIB_LIST_H_ +#define SHADOW_INCLUDE_LIB_LIST_H_ + + +#include "config.h" + +#include +#include + + +extern /*@only@*/char **add_list (/*@returned@*/ /*@only@*/char **, const char *); +extern /*@only@*/char **del_list (/*@returned@*/ /*@only@*/char **, const char *); +extern /*@only@*/char **dup_list (char *const *); +extern void free_list (char **); +extern bool is_on_list (char *const *list, const char *member); +extern /*@only@*/char **comma_to_list (const char *); +extern char **acsv2ls(char *s); +extern int csv2ls(char *s, size_t n, char *ls[restrict n]); + + +#endif // include guard diff --git a/lib/lockpw.c b/lib/lockpw.c index 4f357b48b9..a3cb01a740 100644 --- a/lib/lockpw.c +++ b/lib/lockpw.c @@ -80,6 +80,4 @@ int ulckpwdf (void) return (pw_unlock (true) && spw_unlock (true))? 0 : -1; } -#else -extern int ISO_C_forbids_an_empty_translation_unit; #endif diff --git a/lib/nscd.c b/lib/nscd.c index e2bb44e3fe..533624a40c 100644 --- a/lib/nscd.c +++ b/lib/nscd.c @@ -52,7 +52,4 @@ int nscd_flush_cache (const char *service) return 0; } -#else /* USE_NSCD */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* USE_NSCD */ - diff --git a/lib/pam_pass.c b/lib/pam_pass.c index 5df10f199a..edfcf7a85f 100644 --- a/lib/pam_pass.c +++ b/lib/pam_pass.c @@ -57,6 +57,4 @@ void do_pam_passwd (const char *user, bool silent, bool change_expired) fputs (_("passwd: password updated successfully\n"), shadow_logfd); (void) pam_end (pamh, PAM_SUCCESS); } -#else /* !USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !USE_PAM */ diff --git a/lib/pam_pass_non_interactive.c b/lib/pam_pass_non_interactive.c index 054077f392..38c27eaa80 100644 --- a/lib/pam_pass_non_interactive.c +++ b/lib/pam_pass_non_interactive.c @@ -143,6 +143,4 @@ int do_pam_passwd_non_interactive (const char *pam_service, return ((PAM_SUCCESS == ret) ? 0 : 1); } -#else /* !USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !USE_PAM */ diff --git a/lib/prototypes.h b/lib/prototypes.h index d1fb9b8789..bf26517463 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -192,14 +192,6 @@ void audit_logger_with_group(int type, const char *op, const char *name, extern void setup_limits (const struct passwd *); #endif -/* list.c */ -extern /*@only@*/char **add_list (/*@returned@*/ /*@only@*/char **, const char *); -extern /*@only@*/char **del_list (/*@returned@*/ /*@only@*/char **, const char *); -extern /*@only@*/char **dup_list (char *const *); -extern void free_list (char **); -extern bool is_on_list (char *const *list, const char *member); -extern /*@only@*/char **comma_to_list (const char *); - #ifdef ENABLE_LASTLOG /* log.c */ extern void dolastlog ( diff --git a/lib/pwauth.c b/lib/pwauth.c index 0f0d2ff77c..55a7dac555 100644 --- a/lib/pwauth.c +++ b/lib/pwauth.c @@ -158,6 +158,4 @@ pw_auth(const char *cipher, const char *user) return retval; } -#else /* !USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !USE_PAM */ diff --git a/lib/pwdcheck.c b/lib/pwdcheck.c index cd85aab68e..915fc998a2 100644 --- a/lib/pwdcheck.c +++ b/lib/pwdcheck.c @@ -36,6 +36,4 @@ passwd_check(const char *user, const char *passwd) exit (EXIT_FAILURE); } } -#else /* USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* USE_PAM */ diff --git a/lib/selinux.c b/lib/selinux.c index 0f6ffaf721..09f105a26a 100644 --- a/lib/selinux.c +++ b/lib/selinux.c @@ -204,7 +204,4 @@ int check_selinux_permit (const char *perm_name) freecon (user_context_raw); return r; } - -#else /* !WITH_SELINUX */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !WITH_SELINUX */ diff --git a/lib/semanage.c b/lib/semanage.c index e20fea6ef0..39fa7394ac 100644 --- a/lib/semanage.c +++ b/lib/semanage.c @@ -363,6 +363,4 @@ int del_seuser (const char *login_name) semanage_handle_destroy (handle); return ret; } -#else /* !WITH_SELINUX */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !WITH_SELINUX */ diff --git a/lib/sgroupio.c b/lib/sgroupio.c index ad3adc3463..ccbee93477 100644 --- a/lib/sgroupio.c +++ b/lib/sgroupio.c @@ -56,7 +56,8 @@ return NULL; } - for (i = 0; NULL != sgent->sg_adm[i]; i++); + for (i = 0; NULL != sgent->sg_adm[i]; i++) + continue; /*@-mustfreeonly@*/ sg->sg_adm = malloc_T(i + 1, char *); /*@=mustfreeonly@*/ @@ -81,7 +82,8 @@ } sg->sg_adm[i] = NULL; - for (i = 0; NULL != sgent->sg_mem[i]; i++); + for (i = 0; NULL != sgent->sg_mem[i]; i++) + continue; /*@-mustfreeonly@*/ sg->sg_mem = malloc_T(i + 1, char *); /*@=mustfreeonly@*/ @@ -308,6 +310,4 @@ int sgr_sort () { return commonio_sort_wrt (&gshadow_db, __gr_get_db ()); } -#else -extern int ISO_C_forbids_an_empty_translation_unit; #endif diff --git a/lib/shadow/group/sgetgrent.c b/lib/shadow/group/sgetgrent.c index 2a13091ef0..b8140894fe 100644 --- a/lib/shadow/group/sgetgrent.c +++ b/lib/shadow/group/sgetgrent.c @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 1996-1998, Marek Michałkiewicz // SPDX-FileCopyrightText: 2005, Tomasz Kłoczko // SPDX-FileCopyrightText: 2008, Nicolas François -// SPDX-FileCopyrightText: 2024, Alejandro Colomar +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar // SPDX-License-Identifier: BSD-3-Clause @@ -10,7 +10,9 @@ #include "shadow/group/sgetgrent.h" +#include #include +#include #include #include #include @@ -18,74 +20,80 @@ #include "alloc/malloc.h" #include "atoi/getnum.h" -#include "defines.h" -#include "prototypes.h" -#include "string/strcmp/streq.h" +#include "list.h" +#include "string/strchr/strchrcnt.h" +#include "string/strcpy/stpecpy.h" #include "string/strtok/stpsep.h" #include "string/strtok/strsep2arr.h" -#include "string/strtok/astrsep2ls.h" - - -/* - * list - turn a comma-separated string into an array of (char *)'s - * - * list() converts the comma-separated list of member names into - * an array of character pointers. - * - * FINALLY added dynamic allocation. Still need to fix sgetsgent(). - * --marekm - */ -static char ** -list(char *s) -{ - static char **members = NULL; - - size_t n; - - free(members); - - members = astrsep2ls(s, ",", &n); - if (members == NULL) - return NULL; - - if (streq(members[n-1], "")) - members[n-1] = NULL; - - return members; -} +#include "typetraits.h" // from-string get group entry struct group * sgetgrent(const char *s) { - static char *dup = NULL; - static struct group grent; + static char *buf = NULL; + static struct group grent = {}; - char *fields[4]; + int e; + size_t n, lssize, size; - free(dup); - dup = strdup(s); - if (dup == NULL) - return NULL; + n = strchrcnt(s, ',') + 2; + lssize = n * sizeof(char *); // For 'grent.gr_mem'. + size = lssize + strlen(s) + 1; - stpsep(dup, "\n"); - - if (strsep2arr_a(dup, ":", fields) == -1) - return NULL; - - if (streq(fields[2], "")) + free(buf); + buf = MALLOC(size, char); + if (buf == NULL) return NULL; - grent.gr_name = fields[0]; - grent.gr_passwd = fields[1]; - if (get_gid(fields[2], &grent.gr_gid) == -1) { + e = sgetgrent_r(s, &grent, buf, size); + if (e != 0) { + errno = e; return NULL; } - grent.gr_mem = list(fields[3]); - if (NULL == grent.gr_mem) { - return NULL; /* out of memory */ - } return &grent; } + + +// from-string get group entry re-entrant +int +sgetgrent_r(size_t size; + const char *restrict s, struct group *restrict grent, + char buf[restrict size], size_t size) +{ + char *p, *end; + char *fields[4]; + size_t n, lssize; + + // The first 'lssize' bytes of 'buf' are used for 'grent->gr_mem'. + n = strchrcnt(s, ',') + 2; + lssize = n * sizeof(char *); + if (lssize >= size) + return E2BIG; + + // The remaining bytes of 'buf' are used for a copy of 's'. + end = buf + size; + p = buf + lssize; + if (stpecpy(p, end, s) == NULL) + return errno; + + stpsep(p, "\n"); + + if (strsep2arr_a(p, ":", fields) == -1) + return EINVAL; + + grent->gr_name = fields[0]; + grent->gr_passwd = fields[1]; + if (get_gid(fields[2], &grent->gr_gid) == -1) + return errno; + + if (!is_aligned(buf, char *)) + return EINVAL; + grent->gr_mem = (char **) buf; + if (csv2ls(fields[3], n, grent->gr_mem) == -1) + return errno; + + return 0; +} diff --git a/lib/shadow/group/sgetgrent.h b/lib/shadow/group/sgetgrent.h index fcf0166055..d1be77d0d8 100644 --- a/lib/shadow/group/sgetgrent.h +++ b/lib/shadow/group/sgetgrent.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024, Alejandro Colomar +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar // SPDX-License-Identifier: BSD-3-Clause @@ -9,9 +9,13 @@ #include "config.h" #include +#include struct group *sgetgrent(const char *s); +int sgetgrent_r(size_t size; + const char *restrict s, struct group *restrict grent, + char buf[restrict size], size_t size); #endif // include guard diff --git a/lib/shadow/gshadow/sgetsgent.c b/lib/shadow/gshadow/sgetsgent.c index a239b45ad9..640c62752a 100644 --- a/lib/shadow/gshadow/sgetsgent.c +++ b/lib/shadow/gshadow/sgetsgent.c @@ -10,66 +10,103 @@ #include "shadow/gshadow/sgetsgent.h" +#include #include #include #include +#include "alloc/malloc.h" +#include "list.h" #include "shadow/gshadow/sgrp.h" +#include "string/strchr/strchrcnt.h" #include "string/strcmp/streq.h" -#include "string/strtok/astrsep2ls.h" +#include "string/strcpy/stpecpy.h" #include "string/strtok/stpsep.h" #include "string/strtok/strsep2arr.h" +#include "typetraits.h" #if defined(SHADOWGRP) && !__has_include() -static struct sgrp sgroup = {}; - - -static char **build_list(char *s); - - // from-string get shadow group entry struct sgrp * sgetsgent(const char *s) { - static char *dup = NULL; - - char *fields[4]; + static char *buf = NULL; + static struct sgrp sgent = {}; - free(dup); - dup = strdup(s); - if (dup == NULL) - return NULL; + int e; + size_t n, lssize, size; + struct sgrp *dummy; - stpsep(dup, "\n"); + n = strchrcnt(s, ',') + 4; + lssize = n * sizeof(char *); // For 'sgent.sg_adm' and 'sgent.sg_mem' + size = lssize + strlen(s) + 1; - if (strsep2arr_a(dup, ":", fields) == -1) + free(buf); + buf = MALLOC(size, char); + if (buf == NULL) return NULL; - sgroup.sg_namp = fields[0]; - sgroup.sg_passwd = fields[1]; - - free(sgroup.sg_adm); - free(sgroup.sg_mem); - - sgroup.sg_adm = build_list(fields[2]); - sgroup.sg_mem = build_list(fields[3]); + e = sgetsgent_r(s, &sgent, buf, size, &dummy); + if (e != 0) { + errno = e; + return NULL; + } - return &sgroup; + return &sgent; } -static char ** -build_list(char *s) +// from-string get shadow group entry re-entrant +int +sgetsgent_r(size_t size; + const char *s, struct sgrp *sgent, char buf[size], size_t size, + struct sgrp **dummy) { - char **l; - size_t n; - - l = xastrsep2ls(s, ",", &n); - - if (streq(l[n-1], "")) - l[n-1] = NULL; - - return l; + char *fields[4]; + char *p, *end; + size_t n, nadm, nmem, lssize; + + // 'dummy' exists only for historic reasons. + if (dummy != NULL) + *dummy = sgent; + + // The first 'lssize' bytes of 'buf' are used for 'sg_adm' and 'sg_mem'. + n = strchrcnt(s, ',') + 4; + lssize = n * sizeof(char *); + if (lssize >= size) + return E2BIG; + + // The remaining bytes of 'buf' are used for a copy of 's'. + end = buf + size; + p = buf + lssize; + if (stpecpy(p, end, s) == NULL) + return errno; + + stpsep(p, "\n"); + + if (strsep2arr_a(p, ":", fields) == -1) + return EINVAL; + + sgent->sg_namp = fields[0]; + sgent->sg_passwd = fields[1]; + + if (!is_aligned(buf, char *)) + return EINVAL; + sgent->sg_adm = (char **) buf; + nadm = strchrcnt(fields[2], ',') + 2; + if (nadm > n) + return E2BIG; + if (csv2ls(fields[2], nadm, sgent->sg_adm) == -1) + return errno; + + sgent->sg_mem = sgent->sg_adm + nadm; + nmem = strchrcnt(fields[3], ',') + 2; + if (nmem + nadm > n) + return E2BIG; + if (csv2ls(fields[3], nmem, sgent->sg_mem) == -1) + return errno; + + return 0; } #endif diff --git a/lib/shadow/gshadow/sgetsgent.h b/lib/shadow/gshadow/sgetsgent.h index 65fda800df..19b2e686cf 100644 --- a/lib/shadow/gshadow/sgetsgent.h +++ b/lib/shadow/gshadow/sgetsgent.h @@ -13,11 +13,16 @@ #include "shadow/gshadow/sgrp.h" +#include + #if __has_include() # include #else struct sgrp *sgetsgent(const char *); +int sgetsgent_r(size_t size; + const char *s, struct sgrp *sgent, char buf[size], size_t size, + struct sgrp **dummy); #endif diff --git a/lib/shadow/passwd/sgetpwent.c b/lib/shadow/passwd/sgetpwent.c index 1d4a060708..75fcc6c07f 100644 --- a/lib/shadow/passwd/sgetpwent.c +++ b/lib/shadow/passwd/sgetpwent.c @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 1996-1998, Marek Michałkiewicz // SPDX-FileCopyrightText: 2003-2005, Tomasz Kłoczko // SPDX-FileCopyrightText: 2008, Nicolas François -// SPDX-FileCopyrightText: 2024, Alejandro Colomar +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar // SPDX-License-Identifier: BSD-3-Clause @@ -11,77 +11,75 @@ #include "shadow/passwd/sgetpwent.h" #include +#include #include #include #include #include +#include "alloc/malloc.h" #include "atoi/getnum.h" #include "defines.h" #include "prototypes.h" #include "shadowlog_internal.h" #include "string/strcmp/streq.h" +#include "string/strcpy/strtcpy.h" #include "string/strtok/stpsep.h" #include "string/strtok/strsep2arr.h" -/* - * sgetpwent - convert a string to a (struct passwd) - * - * sgetpwent() parses a string into the parts required for a password - * structure. Strict checking is made for the UID and GID fields and - * presence of the correct number of colons. Any failing tests result - * in a NULL pointer being returned. - * - * NOTE: This function uses hard-coded string scanning functions for - * performance reasons. I am going to come up with some conditional - * compilation glarp to improve on this in the future. - */ // from-string get pasword entry struct passwd * sgetpwent(const char *s) { - static char *dup = NULL; - static struct passwd pwent; + static char *buf = NULL; + static struct passwd pwent = {}; - char *fields[7]; - - free(dup); - dup = strdup(s); - if (dup == NULL) - return NULL; + int e; + size_t size; - stpsep(dup, "\n"); - - if (strsep2arr_a(dup, ":", fields) == -1) - return NULL; + size = strlen(s) + 1; - /* - * The UID and GID must be non-blank. - */ - if (streq(fields[2], "")) + free(buf); + buf = MALLOC(size, char); + if (buf == NULL) return NULL; - if (streq(fields[3], "")) - return NULL; - - /* - * Each of the fields is converted to the appropriate data type - * and the result assigned to the password structure. If the - * UID or GID does not convert to an integer value, a NULL - * pointer is returned. - */ - pwent.pw_name = fields[0]; - pwent.pw_passwd = fields[1]; - if (get_uid(fields[2], &pwent.pw_uid) == -1) { + e = sgetpwent_r(s, &pwent, buf, size); + if (e != 0) { + errno = e; return NULL; } - if (get_gid(fields[3], &pwent.pw_gid) == -1) { - return NULL; - } - pwent.pw_gecos = fields[4]; - pwent.pw_dir = fields[5]; - pwent.pw_shell = fields[6]; return &pwent; } + + +// from-string get pasword entry re-entrant +int +sgetpwent_r(size_t size; + const char *restrict s, struct passwd *restrict pwent, + char buf[restrict size], size_t size) +{ + char *fields[7]; + + if (strtcpy(buf, s, size) == -1) + return errno; + + stpsep(buf, "\n"); + + if (strsep2arr_a(buf, ":", fields) == -1) + return EINVAL; + + pwent->pw_name = fields[0]; + pwent->pw_passwd = fields[1]; + if (get_uid(fields[2], &pwent->pw_uid) == -1) + return errno; + if (get_gid(fields[3], &pwent->pw_gid) == -1) + return errno; + pwent->pw_gecos = fields[4]; + pwent->pw_dir = fields[5]; + pwent->pw_shell = fields[6]; + + return 0; +} diff --git a/lib/shadow/passwd/sgetpwent.h b/lib/shadow/passwd/sgetpwent.h index 7b82a5ce83..f0c12987a1 100644 --- a/lib/shadow/passwd/sgetpwent.h +++ b/lib/shadow/passwd/sgetpwent.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024, Alejandro Colomar +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar // SPDX-License-Identifier: BSD-3-Clause @@ -9,9 +9,13 @@ #include "config.h" #include +#include struct passwd *sgetpwent(const char *s); +int sgetpwent_r(size_t size; + const char *restrict s, struct passwd *restrict pwent, + char buf[restrict size], size_t size); #endif // include guard diff --git a/lib/shadow/shadow/sgetspent.c b/lib/shadow/shadow/sgetspent.c index bd778cdc1d..7ce4f8e2da 100644 --- a/lib/shadow/shadow/sgetspent.c +++ b/lib/shadow/shadow/sgetspent.c @@ -10,8 +10,8 @@ #include "shadow/shadow/sgetspent.h" -#ifndef HAVE_SGETSPENT - +#include +#include #include #include #include @@ -19,11 +19,13 @@ #include #include "atoi/a2i.h" +#include "alloc/malloc.h" #include "defines.h" #include "prototypes.h" #include "shadowlog_internal.h" #include "sizeof.h" #include "string/strcmp/streq.h" +#include "string/strcpy/strtcpy.h" #include "string/strtok/stpsep.h" #include "string/strtok/strsep2arr.h" @@ -32,123 +34,117 @@ #define OFIELDS 5 -/* - * sgetspent - convert string in shadow file format to (struct spwd *) - */ +#ifndef HAVE_SGETSPENT +// from-string get shadow password entry struct spwd * sgetspent(const char *s) { - static char *dup = NULL; - static struct spwd spwd; + static char *buf = NULL; + static struct spwd spent = {}; - char *fields[FIELDS]; - size_t i; + int e; + size_t size; + struct spwd *dummy; + + size = strlen(s) + 1; + + free(buf); + buf = MALLOC(size, char); + if (buf == NULL) + return NULL; - free(dup); - dup = strdup(s); - if (dup == NULL) + e = sgetspent_r(s, &spent, buf, size, &dummy); + if (e != 0) { + errno = e; return NULL; + } + + return &spent; +} +#endif + + +#ifndef HAVE_SGETSPENT_R +// from-string get shadow password entry re-entrant +int +sgetspent_r(size_t size; + const char *restrict s, struct spwd *spent, char buf[size], size_t size, + struct spwd **dummy) +{ + char *fields[FIELDS]; + size_t i; + + // 'dummy' exists only for historic reasons. + if (dummy != NULL) + *dummy = spent; + + if (strtcpy(buf, s, size) == -1) + return errno; - stpsep(dup, "\n"); + stpsep(buf, "\n"); - i = strsep2arr(dup, ":", countof(fields), fields); + i = strsep2arr(buf, ":", countof(fields), fields); if (i == countof(fields) - 1) fields[i++] = ""; if (i != countof(fields) && i != OFIELDS) - return NULL; + return EINVAL; - /* - * Start populating the structure. The fields are all in - * static storage, as is the structure we pass back. - */ - - spwd.sp_namp = fields[0]; - spwd.sp_pwdp = fields[1]; - - /* - * Get the last changed date. For all of the integer fields, - * we check for proper format. It is an error to have an - * incorrectly formatted number. - */ + spent->sp_namp = fields[0]; + spent->sp_pwdp = fields[1]; if (streq(fields[2], "")) - spwd.sp_lstchg = -1; - else if (a2sl(&spwd.sp_lstchg, fields[2], NULL, 0, 0, LONG_MAX) == -1) - return NULL; - - /* - * Get the minimum period between password changes. - */ + spent->sp_lstchg = -1; + else if (a2sl(&spent->sp_lstchg, fields[2], NULL, 0, 0, LONG_MAX) == -1) + return errno; if (streq(fields[3], "")) - spwd.sp_min = -1; - else if (a2sl(&spwd.sp_min, fields[3], NULL, 0, 0, LONG_MAX) == -1) - return NULL; - - /* - * Get the maximum number of days a password is valid. - */ + spent->sp_min = -1; + else if (a2sl(&spent->sp_min, fields[3], NULL, 0, 0, LONG_MAX) == -1) + return errno; if (streq(fields[4], "")) - spwd.sp_max = -1; - else if (a2sl(&spwd.sp_max, fields[4], NULL, 0, 0, LONG_MAX) == -1) - return NULL; - - /* - * If there are only OFIELDS fields (this is a SVR3.2 /etc/shadow - * formatted file), initialize the other field members to -1. - */ + spent->sp_max = -1; + else if (a2sl(&spent->sp_max, fields[4], NULL, 0, 0, LONG_MAX) == -1) + return errno; if (i == OFIELDS) { - spwd.sp_warn = -1; - spwd.sp_inact = -1; - spwd.sp_expire = -1; - spwd.sp_flag = SHADOW_SP_FLAG_UNSET; - - return &spwd; + /* + * If there are only OFIELDS fields, this is a SVr3.2 + * /etc/shadow formatted file. Initialize the other + * field members to -1. + */ + spent->sp_warn = -1; + spent->sp_inact = -1; + spent->sp_expire = -1; + spent->sp_flag = SHADOW_SP_FLAG_UNSET; + + return 0; } - /* - * Get the number of days of password expiry warning. - */ - if (streq(fields[5], "")) - spwd.sp_warn = -1; - else if (a2sl(&spwd.sp_warn, fields[5], NULL, 0, 0, LONG_MAX) == -1) - return NULL; - - /* - * Get the number of days of inactivity before an account is - * disabled. - */ + spent->sp_warn = -1; + else if (a2sl(&spent->sp_warn, fields[5], NULL, 0, 0, LONG_MAX) == -1) + return errno; if (streq(fields[6], "")) - spwd.sp_inact = -1; - else if (a2sl(&spwd.sp_inact, fields[6], NULL, 0, 0, LONG_MAX) == -1) - return NULL; - - /* - * Get the number of days after the epoch before the account is - * set to expire. - */ + spent->sp_inact = -1; + else if (a2sl(&spent->sp_inact, fields[6], NULL, 0, 0, LONG_MAX) == -1) + return errno; if (streq(fields[7], "")) - spwd.sp_expire = -1; - else if (a2sl(&spwd.sp_expire, fields[7], NULL, 0, 0, LONG_MAX) == -1) - return NULL; + spent->sp_expire = -1; + else if (a2sl(&spent->sp_expire, fields[7], NULL, 0, 0, LONG_MAX) == -1) + return errno; /* * This field is reserved for future use. But it isn't supposed * to have anything other than a valid integer in it. */ - if (streq(fields[8], "")) - spwd.sp_flag = SHADOW_SP_FLAG_UNSET; - else if (str2ul(&spwd.sp_flag, fields[8]) == -1) - return NULL; + spent->sp_flag = SHADOW_SP_FLAG_UNSET; + else if (str2ul(&spent->sp_flag, fields[8]) == -1) + return errno; - return (&spwd); + return 0; } -#else -extern int ISO_C_forbids_an_empty_translation_unit; #endif diff --git a/lib/shadow/shadow/sgetspent.h b/lib/shadow/shadow/sgetspent.h index 4f88d08a6b..e1faef4ee4 100644 --- a/lib/shadow/shadow/sgetspent.h +++ b/lib/shadow/shadow/sgetspent.h @@ -9,11 +9,18 @@ #include "config.h" #include +#include #if !defined(HAVE_SGETSPENT) struct spwd *sgetspent(const char *s); #endif +#if !defined(HAVE_SGETSPENT_R) +int sgetspent_r(size_t size; + const char *restrict s, struct spwd *spent, char buf[size], size_t size, + struct spwd **dummy); +#endif + #endif // include guard diff --git a/lib/shadow/subid/sgetsient.c b/lib/shadow/subid/sgetsient.c new file mode 100644 index 0000000000..60f064454a --- /dev/null +++ b/lib/shadow/subid/sgetsient.c @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2012, Eric Biederman +// SPDX-FileCopyrightText: 2025, Alejandro Colomar +// SPDX-License-Identifier: BSD-3-Clause + + +#include "config.h" + +#include "shadow/subid/sgetsient.h" + +#include +#include +#include + +#include "../libsubid/subid.h" + +#include "alloc/malloc.h" +#include "atoi/str2i.h" +#include "string/strcmp/streq.h" +#include "string/strcpy/strtcpy.h" +#include "string/strtok/strsep2arr.h" + + +#ifdef ENABLE_SUBIDS +// sgetsient - from-string get sub-ID entry +struct subordinate_range * +sgetsient(const char *s) +{ + static char *buf = NULL; + static struct subordinate_range sient = {}; + + int e; + size_t size; + + size = strlen(s) + 1; + + free(buf); + buf = MALLOC(size, char); + if (buf == NULL) + return NULL; + + e = sgetsient_r(s, &sient, buf, size); + if (e != 0) { + errno = e; + return NULL; + } + + return &sient; +} + + +// from-string get sub-ID entry re-entrant +int +sgetsient_r(size_t size; + const char *restrict s, struct subordinate_range *restrict sient, + char buf[restrict size], size_t size) +{ + char *fields[SUBID_NFIELDS]; + + if (strtcpy(buf, s, size) == -1) + return errno; + + if (STRSEP2ARR(buf, ":", fields) == -1) + return EINVAL; + + if (streq(fields[0], "")) + return EINVAL; + sient->owner = fields[0]; + if (str2ul(&sient->start, fields[1]) == -1) + return errno; + if (str2ul(&sient->count, fields[2]) == -1) + return errno; + + return 0; +} +#endif diff --git a/lib/shadow/subid/sgetsient.h b/lib/shadow/subid/sgetsient.h new file mode 100644 index 0000000000..5f2a363c1f --- /dev/null +++ b/lib/shadow/subid/sgetsient.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2012, Eric Biederman +// SPDX-FileCopyrightText: 2025, Alejandro Colomar +// SPDX-License-Identifier: BSD-3-Clause + + +#ifndef SHADOW_INCLUDE_LIB_SHADOW_SUBID_SGETSIENT_H_ +#define SHADOW_INCLUDE_LIB_SHADOW_SUBID_SGETSIENT_H_ + + +#include "config.h" + +#include "../libsubid/subid.h" + + +#ifdef ENABLE_SUBIDS +struct subordinate_range *sgetsient(const char *s); +int sgetsient_r(size_t size; + const char *restrict s, struct subordinate_range *restrict sient, + char buf[restrict size], size_t size); +#endif + + +#endif diff --git a/lib/sssd.c b/lib/sssd.c index b915b80a6c..196928b7fd 100644 --- a/lib/sssd.c +++ b/lib/sssd.c @@ -78,7 +78,4 @@ sssd_flush_cache(int dbflags) return 0; } -#else /* USE_SSSD */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* USE_SSSD */ - diff --git a/lib/subordinateio.c b/lib/subordinateio.c index b038379ef3..c9e612fb80 100644 --- a/lib/subordinateio.c +++ b/lib/subordinateio.c @@ -22,10 +22,10 @@ #include "alloc/malloc.h" #include "alloc/reallocf.h" #include "atoi/a2i.h" +#include "shadow/subid/sgetsient.h" #include "string/ctype/strisascii/strisdigit.h" #include "string/sprintf/snprintf.h" #include "string/strcmp/streq.h" -#include "string/strtok/strsep2arr.h" #define ID_SIZE 31 @@ -84,34 +84,7 @@ subordinate_free(/*@only@*/void *ent) static void * subordinate_parse(const char *line) { - static struct subordinate_range range; - static char rangebuf[1024]; - char *fields[SUBID_NFIELDS]; - - /* - * Copy the string to a temporary buffer so the substrings can - * be modified to be NULL terminated. - */ - if (strlen(line) >= sizeof(rangebuf)) - return NULL; /* fail if too long */ - strcpy (rangebuf, line); - - if (strsep2arr_a(rangebuf, ":", fields) == -1) - return NULL; - - if (streq(fields[0], "")) - return NULL; - if (streq(fields[1], "")) - return NULL; - if (streq(fields[2], "")) - return NULL; - range.owner = fields[0]; - if (str2ul(&range.start, fields[1]) == -1) - return NULL; - if (str2ul(&range.count, fields[2]) == -1) - return NULL; - - return ⦥ + return sgetsient(line); } /* @@ -1155,8 +1128,4 @@ void free_subid_pointer(void *ptr) free(ptr); } } - -#else /* !ENABLE_SUBIDS */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !ENABLE_SUBIDS */ - diff --git a/lib/typetraits.h b/lib/typetraits.h index 051edfc66f..1943c9ea72 100644 --- a/lib/typetraits.h +++ b/lib/typetraits.h @@ -8,9 +8,17 @@ #include "config.h" +#include +#include + #include "sizeof.h" +#define is_aligned(p, T) \ +( \ + !((uintptr_t) p % alignof(T)) \ +) + #define is_unsigned(x) \ ( \ (typeof(x)) -1 > 1 \ diff --git a/lib/tz.c b/lib/tz.c index 78f6593cfe..52c1abb032 100644 --- a/lib/tz.c +++ b/lib/tz.c @@ -50,7 +50,4 @@ return result; } -#else /* !USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !USE_PAM */ - diff --git a/src/chgpasswd.c b/src/chgpasswd.c index 9d87bb17bd..d3dec93807 100644 --- a/src/chgpasswd.c +++ b/src/chgpasswd.c @@ -25,6 +25,7 @@ #endif /* ACCT_TOOLS_SETUID */ #include "atoi/a2i.h" #include "defines.h" +#include "list.h" #include "nscd.h" #include "sssd.h" #include "prototypes.h" diff --git a/src/gpasswd.c b/src/gpasswd.c index 0c364321d9..7b2f9a4b59 100644 --- a/src/gpasswd.c +++ b/src/gpasswd.c @@ -27,6 +27,7 @@ /*@-exitarg@*/ #include "exitcodes.h" #include "groupio.h" +#include "list.h" #include "nscd.h" #include "prototypes.h" #ifdef SHADOWGRP diff --git a/src/groupadd.c b/src/groupadd.c index 7bb946b3c1..3ef2f86ac0 100644 --- a/src/groupadd.c +++ b/src/groupadd.c @@ -30,6 +30,7 @@ #include "defines.h" #include "getdef.h" #include "groupio.h" +#include "list.h" #include "nscd.h" #include "sssd.h" #include "prototypes.h" diff --git a/src/groupdel.c b/src/groupdel.c index 2ef4fd8c6c..c5c736b834 100644 --- a/src/groupdel.c +++ b/src/groupdel.c @@ -284,7 +284,8 @@ static void group_busy (gid_t gid) prefix_setpwent (); - while ( ((pwd = prefix_getpwent ()) != NULL) && (pwd->pw_gid != gid) ); + while ( ((pwd = prefix_getpwent ()) != NULL) && (pwd->pw_gid != gid) ) + continue; prefix_endpwent (); diff --git a/src/groupmems.c b/src/groupmems.c index d8fd424418..b459a6a229 100644 --- a/src/groupmems.c +++ b/src/groupmems.c @@ -23,6 +23,7 @@ #include "attr.h" #include "defines.h" #include "groupio.h" +#include "list.h" #include "prototypes.h" #ifdef SHADOWGRP #include "sgroupio.h" diff --git a/src/groupmod.c b/src/groupmod.c index 22d07fe69b..5366206b89 100644 --- a/src/groupmod.c +++ b/src/groupmod.c @@ -32,6 +32,7 @@ #include "chkname.h" #include "defines.h" #include "groupio.h" +#include "list.h" #include "nscd.h" #include "prototypes.h" #include "pwio.h" diff --git a/src/login_nopam.c b/src/login_nopam.c index a41a3f0bd3..f79e52ad1d 100644 --- a/src/login_nopam.c +++ b/src/login_nopam.c @@ -339,7 +339,4 @@ static bool string_match (const char *tok, const char *string) } return false; } - -#else /* !USE_PAM */ -extern int ISO_C_forbids_an_empty_translation_unit; #endif /* !USE_PAM */ diff --git a/src/logoutd.c b/src/logoutd.c index c87b75cbd1..5949e98d36 100644 --- a/src/logoutd.c +++ b/src/logoutd.c @@ -129,7 +129,8 @@ main(int argc, char *[]) (void) textdomain (PACKAGE); #ifndef DEBUG - for (int i = 0; close(i) == 0; i++); + for (int i = 0; close(i) == 0; i++) + continue; setpgrp (); @@ -244,7 +245,8 @@ main(int argc, char *[]) /* * Reap any dead babies ... */ - while (wait(NULL) != -1); + while (wait(NULL) != -1) + continue; } return EXIT_FAILURE; diff --git a/src/newgrp.c b/src/newgrp.c index 77e57be509..9af743d0ef 100644 --- a/src/newgrp.c +++ b/src/newgrp.c @@ -24,6 +24,7 @@ /*@-exitarg@*/ #include "exitcodes.h" #include "getdef.h" +#include "list.h" #include "prototypes.h" #include "search/l/lfind.h" #include "search/l/lsearch.h" diff --git a/src/su.c b/src/su.c index e9b3f0f7bb..6b9d038942 100644 --- a/src/su.c +++ b/src/su.c @@ -53,6 +53,7 @@ /*@-exitarg@*/ #include "exitcodes.h" #include "getdef.h" +#include "list.h" #ifdef USE_PAM #include "pam_defs.h" #endif /* USE_PAM */ diff --git a/src/suauth.c b/src/suauth.c index 4b86afb3fc..967535bfe7 100644 --- a/src/suauth.c +++ b/src/suauth.c @@ -18,6 +18,7 @@ #include #include "defines.h" +#include "list.h" #include "prototypes.h" #include "string/strcmp/streq.h" #include "string/strcmp/strprefix.h" diff --git a/src/useradd.c b/src/useradd.c index c179ff4aad..219161d7d0 100644 --- a/src/useradd.c +++ b/src/useradd.c @@ -47,6 +47,7 @@ #include "fs/mkstemp/fmkomstemp.h" #include "getdef.h" #include "groupio.h" +#include "list.h" #include "nscd.h" #include "prototypes.h" #include "pwauth.h" diff --git a/src/userdel.c b/src/userdel.c index fd211d54dc..2a901a3843 100644 --- a/src/userdel.c +++ b/src/userdel.c @@ -28,6 +28,7 @@ #include "defines.h" #include "getdef.h" #include "groupio.h" +#include "list.h" #include "nscd.h" #include "sssd.h" #include "prototypes.h" diff --git a/src/usermod.c b/src/usermod.c index 074b41fe20..706157f53d 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -42,6 +42,7 @@ #include "faillog.h" #include "getdef.h" #include "groupio.h" +#include "list.h" #include "nscd.h" #include "prototypes.h" #include "pwauth.h"