New upstream version 4.15.1

This commit is contained in:
Chris Hofstaedtler
2024-06-13 03:08:44 +02:00
parent 3987cc7880
commit f7f4fd7c05
759 changed files with 53132 additions and 39788 deletions
+143 -12
View File
@@ -5,57 +5,127 @@ DEFS =
noinst_LTLIBRARIES = libshadow.la
if USE_PAM
LIBCRYPT_PAM = $(LIBCRYPT)
else
LIBCRYPT_PAM =
endif
AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir) $(ECONF_CPPFLAGS)
libshadow_la_CPPFLAGS = $(ECONF_CPPFLAGS)
if HAVE_VENDORDIR
libshadow_la_CPPFLAGS += -DVENDORDIR=\"$(VENDORDIR)\"
endif
libshadow_la_CPPFLAGS += -I$(top_srcdir)
libshadow_la_CFLAGS = $(LIBBSD_CFLAGS) $(LIBCRYPT_PAM) $(LIBSYSTEMD)
libshadow_la_LIBADD = $(LIBADD_DLOPEN)
libshadow_la_SOURCES = \
addgrps.c \
adds.c \
adds.h \
age.c \
agetpass.c \
agetpass.h \
alloc.c \
alloc.h \
atoi/strtoi.c \
atoi/strtoi.h \
atoi/strtou_noneg.c \
atoi/strtou_noneg.h \
attr.h \
audit_help.c \
basename.c \
bit.c \
bit.h \
cast.h \
chkname.c \
chkname.h \
chowndir.c \
chowntty.c \
cleanup.c \
cleanup_group.c \
cleanup_user.c \
commonio.c \
commonio.h \
console.c \
copydir.c \
csrand.c \
defines.h \
encrypt.c \
env.c \
exitcodes.h \
faillog.h \
failure.c \
failure.h \
fd.c \
fields.c \
find_new_gid.c \
find_new_uid.c \
find_new_sub_gids.c \
find_new_sub_uids.c \
fputsx.c \
getdef.c \
getdef.h \
get_gid.c \
getlong.c \
get_pid.c \
get_uid.c \
getdate.h \
getdate.y \
getdef.c \
getdef.h \
getlong.c \
getgr_nam_gid.c \
getrange.c \
gettime.c \
getulong.c \
groupio.c \
groupmem.c \
groupio.h \
gshadow.c \
hushed.c \
idmapping.h \
idmapping.c \
isexpired.c \
limits.c \
list.c \
lockpw.c \
loginprompt.c \
mail.c \
memzero.c \
memzero.h \
motd.c \
must_be.h \
myname.c \
nss.c \
nscd.c \
nscd.h \
shadowlog.c \
shadowlog.h \
shadowlog_internal.h \
sssd.c \
sssd.h \
obscure.c \
pam_defs.h \
pam_pass.c \
pam_pass_non_interactive.c \
port.c \
port.h \
prefix_flag.c \
prototypes.h \
pwauth.c \
pwauth.h \
pwio.c \
pwio.h \
pwd_init.c \
pwd2spwd.c \
pwdcheck.c \
pwmem.c \
remove_tree.c \
rlogin.c \
root_flag.c \
run_part.h \
run_part.c \
subordinateio.h \
subordinateio.c \
salt.c \
selinux.c \
semanage.c \
setugid.c \
setupenv.c \
sgetgrent.c \
sgetpwent.c \
sgetspent.c \
@@ -64,14 +134,74 @@ libshadow_la_SOURCES = \
shadow.c \
shadowio.c \
shadowio.h \
shadowlog.c \
shadowlog.h \
shadowlog_internal.h \
shadowmem.c \
shell.c \
sizeof.h \
spawn.c \
utent.c
sssd.c \
sssd.h \
string/sprintf.c \
string/sprintf.h \
string/stpecpy.c \
string/stpecpy.h \
string/stpeprintf.c \
string/stpeprintf.h \
string/strftime.c \
string/strftime.h \
string/strncpy.h \
string/strtcpy.c \
string/strtcpy.h \
string/zustr2stp.h \
strtoday.c \
sub.c \
subordinateio.h \
subordinateio.c \
sulog.c \
time/day_to_str.c \
time/day_to_str.h \
ttytype.c \
tz.c \
ulimit.c \
user_busy.c \
valid.c \
write_full.c \
xgetpwnam.c \
xprefix_getpwnam.c \
xgetpwuid.c \
xgetgrnam.c \
xgetgrgid.c \
xgetspnam.c \
yesno.c
if WITH_TCB
libshadow_la_SOURCES += tcbfuncs.c tcbfuncs.h
endif
if WITH_BTRFS
libshadow_la_SOURCES += btrfs.c
endif
if ENABLE_LASTLOG
libshadow_la_SOURCES += log.c
endif
if ENABLE_LOGIND
libshadow_la_SOURCES += logind.c
else
libshadow_la_SOURCES += utmp.c
endif
if !WITH_LIBBSD
libshadow_la_SOURCES += \
freezero.h \
freezero.c \
readpassphrase.h \
readpassphrase.c
endif
# These files are unneeded for some reason, listed in
# order of appearance:
#
@@ -79,4 +209,5 @@ endif
EXTRA_DIST = \
.indent.pro \
gshadow_.h
gshadow_.h \
xgetXXbyYY.c
+1237 -151
View File
File diff suppressed because it is too large Load Diff
+114
View File
@@ -0,0 +1,114 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2001 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#if defined (HAVE_SETGROUPS) && ! defined (USE_PAM)
#include "prototypes.h"
#include "defines.h"
#include <stdio.h>
#include <grp.h>
#include <errno.h>
#include "alloc.h"
#include "shadowlog.h"
#ident "$Id$"
#define SEP ",:"
/*
* Add groups with names from LIST (separated by commas or colons)
* to the supplementary group set. Silently ignore groups which are
* already there. Warning: uses strtok().
*/
int add_groups (const char *list)
{
GETGROUPS_T *grouplist;
size_t i;
int ngroups;
bool added;
char *token;
char buf[1024];
int ret;
FILE *shadow_logfd = log_get_logfd();
if (strlen (list) >= sizeof (buf)) {
errno = EINVAL;
return -1;
}
strcpy (buf, list);
i = 16;
for (;;) {
grouplist = MALLOC(i, GETGROUPS_T);
if (NULL == grouplist) {
return -1;
}
ngroups = getgroups (i, grouplist);
if ( ( (-1 == ngroups)
&& (EINVAL != errno))
|| (i > (size_t)ngroups)) {
/* Unexpected failure of getgroups or successful
* reception of the groups */
break;
}
/* not enough room, so try allocating a larger buffer */
free (grouplist);
i *= 2;
}
if (ngroups < 0) {
free (grouplist);
return -1;
}
added = false;
for (token = strtok (buf, SEP); NULL != token; token = strtok (NULL, SEP)) {
struct group *grp;
grp = getgrnam (token); /* local, no need for xgetgrnam */
if (NULL == grp) {
fprintf (shadow_logfd, _("Warning: unknown group %s\n"),
token);
continue;
}
for (i = 0; i < (size_t)ngroups && grouplist[i] != grp->gr_gid; i++);
if (i < (size_t)ngroups) {
continue;
}
if (ngroups >= sysconf (_SC_NGROUPS_MAX)) {
fputs (_("Warning: too many groups\n"), shadow_logfd);
break;
}
grouplist = REALLOCF(grouplist, (size_t) ngroups + 1, GETGROUPS_T);
if (grouplist == NULL) {
return -1;
}
grouplist[ngroups] = grp->gr_gid;
ngroups++;
added = true;
}
if (added) {
ret = setgroups (ngroups, grouplist);
free (grouplist);
return ret;
}
free (grouplist);
return 0;
}
#else /* HAVE_SETGROUPS && !USE_PAM */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* HAVE_SETGROUPS && !USE_PAM */
+15
View File
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "adds.h"
#include <stddef.h>
extern inline long addsl2(long a, long b);
extern inline long addslN(size_t n, long addend[n]);
extern inline int cmpl(const void *p1, const void *p2);
+86
View File
@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ADDS_H_
#define SHADOW_INCLUDE_LIB_ADDS_H_
#include <config.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include "sizeof.h"
#define addsl(a, b, ...) \
({ \
long addend_[] = {a, b, __VA_ARGS__}; \
\
addslN(NITEMS(addend_), addend_); \
})
inline long addsl2(long a, long b);
inline long addslN(size_t n, long addend[n]);
inline int cmpl(const void *p1, const void *p2);
inline long
addsl2(long a, long b)
{
if (a > 0 && b > LONG_MAX - a) {
errno = EOVERFLOW;
return LONG_MAX;
}
if (a < 0 && b < LONG_MIN - a) {
errno = EOVERFLOW;
return LONG_MIN;
}
return a + b;
}
inline long
addslN(size_t n, long addend[n])
{
int e;
if (n == 0) {
errno = EDOM;
return 0;
}
e = errno;
while (n > 1) {
qsort(addend, n, sizeof(addend[0]), cmpl);
errno = 0;
addend[0] = addsl2(addend[0], addend[--n]);
if (errno == EOVERFLOW)
return addend[0];
}
errno = e;
return addend[0];
}
inline int
cmpl(const void *p1, const void *p2)
{
const long *l1 = p1;
const long *l2 = p2;
if (*l1 < *l2)
return -1;
if (*l1 > *l2)
return +1;
return 0;
}
#endif // include guard
+181
View File
@@ -0,0 +1,181 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2001 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include "adds.h"
#include "defines.h"
#include "exitcodes.h"
#include "prototypes.h"
#ident "$Id$"
#ifndef PASSWD_PROGRAM
#define PASSWD_PROGRAM "/bin/passwd"
#endif
/*
* expire - force password change if password expired
*
* expire() calls /bin/passwd to change the user's password
* if it has expired.
*/
int expire (const struct passwd *pw, /*@null@*/const struct spwd *sp)
{
int status;
pid_t child;
pid_t pid;
if (NULL == sp) {
return 0;
}
/*
* See if the user's password has expired, and if so
* force them to change their password.
*/
status = isexpired (pw, sp);
switch (status) {
case 0:
return 0;
case 1:
(void) fputs (_("Your password has expired."), stdout);
break;
case 2:
(void) fputs (_("Your password is inactive."), stdout);
break;
case 3:
(void) fputs (_("Your login has expired."), stdout);
break;
}
/*
* Setting the maximum valid period to less than the minimum
* valid period means that the minimum period will never
* occur while the password is valid, so the user can never
* change that password.
*/
if ((status > 1) || (sp->sp_max < sp->sp_min)) {
(void) puts (_(" Contact the system administrator."));
exit (EXIT_FAILURE);
}
(void) puts (_(" Choose a new password."));
(void) fflush (stdout);
/*
* Close all the files so that unauthorized access won't
* occur. This needs to be done anyway because those files
* might become stale after "passwd" is executed.
*/
endspent ();
endpwent ();
#ifdef SHADOWGRP
endsgent ();
#endif
endgrent ();
/*
* Execute the /bin/passwd command. The exit status will be
* examined to see what the result is. If there are any
* errors the routine will exit. This forces the user to
* change their password before being able to use the account.
*/
pid = fork ();
if (0 == pid) {
int err;
/*
* Set the UID to be that of the user. This causes
* passwd to work just like it would had they executed
* it from the command line while logged in.
*/
#if defined(HAVE_INITGROUPS) && ! defined(USE_PAM)
if (setup_uid_gid (pw, false) != 0)
#else
if (setup_uid_gid (pw) != 0)
#endif
{
_exit (126);
}
(void) execl (PASSWD_PROGRAM, PASSWD_PROGRAM, pw->pw_name, (char *) NULL);
err = errno;
perror ("Can't execute " PASSWD_PROGRAM);
_exit ((ENOENT == err) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
} else if ((pid_t) -1 == pid) {
perror ("fork");
exit (EXIT_FAILURE);
}
while (((child = wait (&status)) != pid) && (child != (pid_t)-1));
if ((child == pid) && (0 == status)) {
return 1;
}
exit (EXIT_FAILURE);
/*@notreached@*/}
/*
* agecheck - see if warning is needed for password expiration
*
* agecheck sees how many days until the user's password is going
* to expire and warns the user of the pending password expiration.
*/
void agecheck (/*@null@*/const struct spwd *sp)
{
long now = time(NULL) / DAY;
long remain;
if (NULL == sp) {
return;
}
/*
* The last, max, and warn fields must be supported or the
* warning period cannot be calculated.
*/
if ( (-1 == sp->sp_lstchg)
|| (-1 == sp->sp_max)
|| (-1 == sp->sp_warn)) {
return;
}
if (0 == sp->sp_lstchg) {
(void) puts (_("You must change your password."));
return;
}
remain = addsl(sp->sp_lstchg, sp->sp_max, -now);
if (remain <= sp->sp_warn) {
if (remain > 1) {
(void) printf (_("Your password will expire in %ld days.\n"),
remain);
} else if (1 == remain) {
(void) puts (_("Your password will expire tomorrow."));
} else if (remain == 0) {
(void) puts (_("Your password will expire today."));
}
}
}
+145
View File
@@ -0,0 +1,145 @@
/*
* SPDX-FileCopyrightText: 2022, Alejandro Colomar <alx@kernel.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "agetpass.h"
#include <limits.h>
#include <readpassphrase.h>
#include <stdlib.h>
#include <string.h>
#ident "$Id$"
#include "alloc.h"
#if WITH_LIBBSD == 0
#include "freezero.h"
#endif /* WITH_LIBBSD */
/*
* SYNOPSIS
* [[gnu::malloc(erase_pass)]]
* char *agetpass(const char *prompt);
* char *agetpass_stdin();
*
* void erase_pass(char *pass);
*
* ARGUMENTS
* agetpass()
* prompt String to be printed before reading a password.
*
* erase_pass()
* pass password previously returned by agetpass().
*
* DESCRIPTION
* agetpass()
* This function is very similar to getpass(3). It has several
* advantages compared to getpass(3):
*
* - Instead of using a static buffer, agetpass() allocates memory
* through malloc(3). This makes the function thread-safe, and
* also reduces the visibility of the buffer.
*
* - agetpass() doesn't reallocate internally. Some
* implementations of getpass(3), such as glibc, do that, as a
* consequence of calling getline(3). That's a bug in glibc,
* which allows leaking prefixes of passwords in freed memory.
*
* - agetpass() doesn't overrun the output buffer. If the input
* password is too long, it simply fails. Some implementations
* of getpass(3), share the same bug that gets(3) has.
*
* As soon as possible, the password obtained from agetpass() be
* erased by calling erase_pass(), to avoid possibly leaking the
* password.
*
* agetpass_stdin()
* This function is the same as previous one (agetpass). Just the
* password is read from stdin and terminal is not required.
*
* erase_pass()
* This function first clears the password, by calling
* explicit_bzero(3) (or an equivalent call), and then frees the
* allocated memory by calling free(3).
*
* NULL is a valid input pointer, and in such a case, this call is
* a no-op.
*
* RETURN VALUE
* agetpass() returns a newly allocated buffer containing the
* password on success. On error, errno is set to indicate the
* error, and NULL is returned.
*
* ERRORS
* agetpass()
* This function may fail for any errors that malloc(3) or
* readpassphrase(3) may fail, and in addition it may fail for the
* following errors:
*
* ENOBUFS
* The input password was longer than PASS_MAX.
*
* CAVEATS
* If a password is passed twice to erase_pass(), the behavior is
* undefined.
*/
static char *
agetpass_internal(const char *prompt, int flags)
{
char *pass;
size_t len;
/*
* Since we want to support passwords upto PASS_MAX, we need
* PASS_MAX bytes for the password itself, and one more byte for
* the terminating '\0'. We also want to detect truncation, and
* readpassphrase(3) doesn't detect it, so we need some trick.
* Let's add one more byte, and if the password uses it, it
* means the introduced password was longer than PASS_MAX.
*/
pass = MALLOC(PASS_MAX + 2, char);
if (pass == NULL)
return NULL;
if (readpassphrase(prompt, pass, PASS_MAX + 2, flags) == NULL)
goto fail;
len = strlen(pass);
if (len == PASS_MAX + 1) {
errno = ENOBUFS;
goto fail;
}
return pass;
fail:
freezero(pass, PASS_MAX + 2);
return NULL;
}
char *
agetpass(const char *prompt)
{
return agetpass_internal(prompt, RPP_REQUIRE_TTY);
}
char *
agetpass_stdin()
{
return agetpass_internal(NULL, RPP_STDIN);
}
void
erase_pass(char *pass)
{
freezero(pass, PASS_MAX + 2);
}
+23
View File
@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2022-2023, Alejandro Colomar <alx@kernel.org>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SHADOW_INCLUDE_LIB_AGETPASS_H_
#define SHADOW_INCLUDE_LIB_AGETPASS_H_
#include <config.h>
#include "attr.h"
#include "defines.h"
void erase_pass(char *pass);
ATTR_MALLOC(erase_pass)
char *agetpass(const char *prompt);
char *agetpass_stdin();
#endif // include guard
+73
View File
@@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
* SPDX-FileCopyrightText: 2023 , Alejandro Colomar <alx@kernel.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* Replacements for malloc and strdup with error checking. Too trivial
to be worth copyrighting :-). I did that because a lot of code used
malloc and strdup without checking for NULL pointer, and I like some
message better than a core dump... --marekm
Yeh, but. Remember that bailing out might leave the system in some
bizarre state. You really want to put in error checking, then add
some back-out failure recovery code. -- jfh */
#include <config.h>
#ident "$Id$"
#include "alloc.h"
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include "defines.h"
#include "prototypes.h"
#include "shadowlog.h"
extern inline void *xmalloc(size_t size);
extern inline void *xmallocarray(size_t nmemb, size_t size);
extern inline void *mallocarray(size_t nmemb, size_t size);
extern inline void *reallocarrayf(void *p, size_t nmemb, size_t size);
extern inline char *xstrdup(const char *str);
void *
xcalloc(size_t nmemb, size_t size)
{
void *p;
p = calloc(nmemb, size);
if (p == NULL)
goto x;
return p;
x:
fprintf(log_get_logfd(), _("%s: %s\n"),
log_get_progname(), strerror(errno));
exit(13);
}
void *
xreallocarray(void *p, size_t nmemb, size_t size)
{
p = reallocarrayf(p, nmemb, size);
if (p == NULL)
goto x;
return p;
x:
fprintf(log_get_logfd(), _("%s: %s\n"),
log_get_progname(), strerror(errno));
exit(13);
}
+116
View File
@@ -0,0 +1,116 @@
/*
* SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SHADOW_INCLUDE_LIB_MALLOC_H_
#define SHADOW_INCLUDE_LIB_MALLOC_H_
#include <config.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "attr.h"
#include "defines.h"
#define CALLOC(n, type) ((type *) calloc(n, sizeof(type)))
#define XCALLOC(n, type) ((type *) xcalloc(n, sizeof(type)))
#define MALLOC(n, type) ((type *) mallocarray(n, sizeof(type)))
#define XMALLOC(n, type) ((type *) xmallocarray(n, sizeof(type)))
#define REALLOC(ptr, n, type) \
({ \
__auto_type p_ = (ptr); \
\
static_assert(__builtin_types_compatible_p(typeof(p_), type *), ""); \
\
(type *) reallocarray(p_, n, sizeof(type)); \
})
#define REALLOCF(ptr, n, type) \
({ \
__auto_type p_ = (ptr); \
\
static_assert(__builtin_types_compatible_p(typeof(p_), type *), ""); \
\
(type *) reallocarrayf(p_, n, sizeof(type)); \
})
#define XREALLOC(ptr, n, type) \
({ \
__auto_type p_ = (ptr); \
\
static_assert(__builtin_types_compatible_p(typeof(p_), type *), ""); \
\
(type *) xreallocarray(p_, n, sizeof(type)); \
})
ATTR_MALLOC(free)
inline void *xmalloc(size_t size);
ATTR_MALLOC(free)
inline void *xmallocarray(size_t nmemb, size_t size);
ATTR_MALLOC(free)
inline void *mallocarray(size_t nmemb, size_t size);
ATTR_MALLOC(free)
inline void *reallocarrayf(void *p, size_t nmemb, size_t size);
ATTR_MALLOC(free)
inline char *xstrdup(const char *str);
ATTR_MALLOC(free)
void *xcalloc(size_t nmemb, size_t size);
ATTR_MALLOC(free)
void *xreallocarray(void *p, size_t nmemb, size_t size);
inline void *
xmalloc(size_t size)
{
return xmallocarray(1, size);
}
inline void *
xmallocarray(size_t nmemb, size_t size)
{
return xreallocarray(NULL, nmemb, size);
}
inline void *
mallocarray(size_t nmemb, size_t size)
{
return reallocarray(NULL, nmemb, size);
}
inline void *
reallocarrayf(void *p, size_t nmemb, size_t size)
{
void *q;
q = reallocarray(p, nmemb, size);
/* realloc(p, 0) is equivalent to free(p); avoid double free. */
if (q == NULL && nmemb != 0 && size != 0)
free(p);
return q;
}
inline char *
xstrdup(const char *str)
{
return strcpy(XMALLOC(strlen(str) + 1, char), str);
}
#endif // include guard
+15
View File
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/strtoi.h"
#include <stdint.h>
extern inline intmax_t strtoi_(const char *s, char **restrict endp, int base,
intmax_t min, intmax_t max, int *restrict status);
extern inline uintmax_t strtou_(const char *s, char **restrict endp, int base,
uintmax_t min, uintmax_t max, int *restrict status);
+96
View File
@@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_STRTOI_H_
#define SHADOW_INCLUDE_LIB_ATOI_STRTOI_H_
#include <config.h>
#include <errno.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/param.h>
#include "attr.h"
#define strtoNmax(TYPE, ...) \
( \
_Generic((TYPE) 0, \
intmax_t: strtoimax, \
uintmax_t: strtoumax \
)(__VA_ARGS__) \
)
#define strtoN(s, endp, base, min, max, status, TYPE) \
({ \
const char *s_ = s; \
char **endp_ = endp; \
int base_ = base; \
TYPE min_ = min; \
TYPE max_ = max; \
int *status_ = status; \
\
int e_, st_; \
char *end_; \
TYPE n_; \
\
if (endp_ == NULL) \
endp_ = &end_; \
if (status_ == NULL) \
status_ = &st_; \
\
if (base_ != 0 && (base_ < 0 || base_ > 36)) { \
*status_ = EINVAL; \
n_ = 0; \
\
} else { \
e_ = errno; \
errno = 0; \
n_ = strtoNmax(TYPE, s_, endp_, base_); \
\
if (*endp_ == s_) \
*status_ = ECANCELED; \
else if (errno == ERANGE || n_ < min_ || n_ > max_) \
*status_ = ERANGE; \
else if (**endp_ != '\0') \
*status_ = ENOTSUP; \
else \
*status_ = 0; \
\
errno = e_; \
} \
MAX(min_, MIN(max_, n_)); \
})
ATTR_STRING(1) ATTR_ACCESS(write_only, 2) ATTR_ACCESS(write_only, 6)
inline intmax_t strtoi_(const char *s, char **restrict endp, int base,
intmax_t min, intmax_t max, int *restrict status);
ATTR_STRING(1) ATTR_ACCESS(write_only, 2) ATTR_ACCESS(write_only, 6)
inline uintmax_t strtou_(const char *s, char **restrict endp, int base,
uintmax_t min, uintmax_t max, int *restrict status);
inline intmax_t
strtoi_(const char *s, char **restrict endp, int base,
intmax_t min, intmax_t max, int *restrict status)
{
return strtoN(s, endp, base, min, max, status, intmax_t);
}
inline uintmax_t
strtou_(const char *s, char **restrict endp, int base,
uintmax_t min, uintmax_t max, int *restrict status)
{
return strtoN(s, endp, base, min, max, status, uintmax_t);
}
#endif // include guard
+18
View File
@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/strtou_noneg.h"
#include <stdint.h>
extern inline uintmax_t strtou_noneg(const char *s, char **restrict endp,
int base, uintmax_t min, uintmax_t max, int *restrict status);
extern inline unsigned long strtoul_noneg(const char *s,
char **restrict endp, int base);
extern inline unsigned long long strtoull_noneg(const char *s,
char **restrict endp, int base);
+68
View File
@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_STRTOU_NONEG_H_
#define SHADOW_INCLUDE_LIB_ATOI_STRTOU_NONEG_H_
#include <config.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include "atoi/strtoi.h"
#include "attr.h"
ATTR_STRING(1) ATTR_ACCESS(write_only, 2) ATTR_ACCESS(write_only, 6)
inline uintmax_t strtou_noneg(const char *s, char **restrict endp,
int base, uintmax_t min, uintmax_t max, int *restrict status);
ATTR_STRING(1) ATTR_ACCESS(write_only, 2)
inline unsigned long strtoul_noneg(const char *s,
char **restrict endp, int base);
ATTR_STRING(1) ATTR_ACCESS(write_only, 2)
inline unsigned long long strtoull_noneg(const char *s,
char **restrict endp, int base);
inline uintmax_t
strtou_noneg(const char *s, char **restrict endp, int base,
uintmax_t min, uintmax_t max, int *restrict status)
{
int st;
if (status == NULL)
status = &st;
if (strtoi_(s, endp, base, 0, 1, status) == 0 && *status == ERANGE)
return min;
return strtou_(s, endp, base, min, max, status);
}
inline unsigned long
strtoul_noneg(const char *s, char **restrict endp, int base)
{
if (strtol(s, endp, base) < 0) {
errno = ERANGE;
return 0;
}
return strtoul(s, endp, base);
}
inline unsigned long long
strtoull_noneg(const char *s, char **restrict endp, int base)
{
if (strtol(s, endp, base) < 0) {
errno = ERANGE;
return 0;
}
return strtoull(s, endp, base);
}
#endif // include guard
+33
View File
@@ -0,0 +1,33 @@
#ifndef SHADOW_INCLUDE_LIB_ATTR_H_
#define SHADOW_INCLUDE_LIB_ATTR_H_
#include "config.h"
#if defined(__GNUC__)
# define MAYBE_UNUSED __attribute__((unused))
# define NORETURN __attribute__((__noreturn__))
# define format_attr(type, fmt, va) __attribute__((format(type, fmt, va)))
# define ATTR_ACCESS(...) __attribute__((access(__VA_ARGS__)))
#else
# define MAYBE_UNUSED
# define NORETURN
# define format_attr(type, fmt, va)
# define ATTR_ACCESS(...)
#endif
#if (__GNUC__ >= 11) && !defined(__clang__)
# define ATTR_MALLOC(deallocator) [[gnu::malloc(deallocator)]]
#else
# define ATTR_MALLOC(deallocator)
#endif
#if (__GNUC__ >= 14)
# define ATTR_STRING(...) [[gnu::null_terminated_string_arg(__VA_ARGS__)]]
#else
# define ATTR_STRING(...)
#endif
#endif // include guard
+89
View File
@@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: 2005 , Red Hat, Inc.
* SPDX-FileCopyrightText: 2005 , Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Audit helper functions used throughout shadow
*
*/
#include <config.h>
#ifdef WITH_AUDIT
#include <stdlib.h>
#include <syslog.h>
#include <stdarg.h>
#include <libaudit.h>
#include <errno.h>
#include <stdio.h>
#include "attr.h"
#include "prototypes.h"
#include "shadowlog.h"
int audit_fd;
void audit_help_open (void)
{
audit_fd = audit_open ();
if (audit_fd < 0) {
/* You get these only when the kernel doesn't have
* audit compiled in. */
if ( (errno == EINVAL)
|| (errno == EPROTONOSUPPORT)
|| (errno == EAFNOSUPPORT)) {
return;
}
(void) fputs (_("Cannot open audit interface - aborting.\n"),
log_get_logfd());
exit (EXIT_FAILURE);
}
}
/*
* This function will log a message to the audit system using a predefined
* message format. Parameter usage is as follows:
*
* type - type of message: AUDIT_USER_CHAUTHTOK for changing any account
* attributes.
* pgname - program's name
* op - operation. "adding user", "changing finger info", "deleting group"
* name - user's account or group name. If not available use NULL.
* id - uid or gid that the operation is being performed on. This is used
* only when user is NULL.
*/
void audit_logger (int type, MAYBE_UNUSED const char *pgname, const char *op,
const char *name, unsigned int id,
shadow_audit_result result)
{
if (audit_fd < 0) {
return;
} else {
audit_log_acct_message (audit_fd, type, NULL, op, name, id,
NULL, NULL, NULL, result);
}
}
void audit_logger_message (const char *message, shadow_audit_result result)
{
if (audit_fd < 0) {
return;
} else {
audit_log_user_message (audit_fd,
AUDIT_USYS_CONFIG,
message,
NULL, /* hostname */
NULL, /* addr */
NULL, /* tty */
result);
}
}
#else /* WITH_AUDIT */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* WITH_AUDIT */
+31
View File
@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1997, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* basename.c - not worth copyrighting :-). Some versions of Linux libc
* already have basename(), other versions don't. To avoid confusion,
* we will not use the function from libc and use a different name here.
* --marekm
*/
#include <config.h>
#ident "$Id$"
#include "defines.h"
#include "prototypes.h"
/*@observer@*/const char *Basename (const char *str)
{
if (str == NULL) {
abort ();
}
char *cp = strrchr (str, '/');
return (NULL != cp) ? cp + 1 : str;
}
+19
View File
@@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2022 - 2023, Alejandro Colomar <alx@kernel.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include "bit.h"
#include <limits.h>
extern inline unsigned long bit_ceilul(unsigned long x);
extern inline unsigned long bit_ceil_wrapul(unsigned long x);
extern inline int leading_zerosul(unsigned long x);
+53
View File
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2022 - 2023, Alejandro Colomar <alx@kernel.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SHADOW_INCLUDE_LIB_BIT_H_
#define SHADOW_INCLUDE_LIB_BIT_H_
#include <config.h>
#include <limits.h>
#ifndef ULONG_WIDTH
#define ULONG_WIDTH (sizeof(unsigned long) * CHAR_BIT)
#endif
inline unsigned long bit_ceilul(unsigned long x);
inline unsigned long bit_ceil_wrapul(unsigned long x);
inline int leading_zerosul(unsigned long x);
/* stdc_bit_ceilul(3) */
inline unsigned long
bit_ceilul(unsigned long x)
{
return 1 + (ULONG_MAX >> leading_zerosul(x));
}
/* stdc_bit_ceilul(3), but wrap instead of having Undefined Behavior */
inline unsigned long
bit_ceil_wrapul(unsigned long x)
{
if (x == 0)
return 0;
return bit_ceilul(x);
}
/* stdc_leading_zerosul(3) */
inline int
leading_zerosul(unsigned long x)
{
return (x == 0) ? ULONG_WIDTH : __builtin_clzl(x);
}
#endif // include guard
+110
View File
@@ -0,0 +1,110 @@
#include <linux/btrfs_tree.h>
#include <linux/magic.h>
#include <sys/statfs.h>
#include <stdbool.h>
#include "prototypes.h"
static bool path_exists(const char *p)
{
struct stat sb;
return stat(p, &sb) == 0;
}
static const char *btrfs_cmd(void)
{
const char *const btrfs_paths[] = {"/sbin/btrfs",
"/bin/btrfs", "/usr/sbin/btrfs", "/usr/bin/btrfs", NULL};
const char *p;
int i;
for (i = 0, p = btrfs_paths[i]; p; i++, p = btrfs_paths[i])
if (path_exists(p))
return p;
return NULL;
}
static int run_btrfs_subvolume_cmd(const char *subcmd, const char *arg1, const char *arg2)
{
int status = 0;
const char *cmd = btrfs_cmd();
const char *argv[] = {
"btrfs",
"subvolume",
subcmd,
arg1,
arg2,
NULL
};
if (!cmd || access(cmd, X_OK)) {
return 1;
}
if (run_command(cmd, argv, NULL, &status))
return -1;
return status;
}
int btrfs_create_subvolume(const char *path)
{
return run_btrfs_subvolume_cmd("create", path, NULL);
}
int btrfs_remove_subvolume(const char *path)
{
return run_btrfs_subvolume_cmd("delete", "-C", path);
}
/* Adapted from btrfsprogs */
/*
* This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
* a file descriptor and calling it, because fstat() and fstatfs() don't accept
* file descriptors opened with O_PATH on old kernels (before v3.6 and before
* v3.12, respectively), but stat() and statfs() can be called on a path that
* the user doesn't have read or write permissions to.
*
* returns:
* 1 - btrfs subvolume
* 0 - not btrfs subvolume
* -1 - error
*/
int btrfs_is_subvolume(const char *path)
{
struct stat st;
int ret;
ret = is_btrfs(path);
if (ret <= 0)
return ret;
ret = stat(path, &st);
if (ret == -1)
return -1;
if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
return 0;
}
return 1;
}
/* Adapted from btrfsprogs */
int is_btrfs(const char *path)
{
struct statfs sfs;
int ret;
ret = statfs(path, &sfs);
if (ret == -1)
return -1;
return sfs.f_type == BTRFS_SUPER_MAGIC;
}
+21
View File
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2022-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_CAST_H_
#define SHADOW_INCLUDE_LIB_CAST_H_
#include <config.h>
#include "must_be.h"
#define const_cast(T, p) \
({ \
static_assert(is_same_type(typeof(&*(p)), const T), ""); \
(T) (p); \
})
#endif // include guard
+109
View File
@@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: 1990-1994, Julianne Frances Haugh
// SPDX-FileCopyrightText: 1996-2000, Marek Michałkiewicz
// SPDX-FileCopyrightText: 2001-2005, Tomasz Kłoczko
// SPDX-FileCopyrightText: 2005-2008, Nicolas François
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
/*
* is_valid_user_name(), is_valid_group_name() - check the new user/group
* name for validity;
* return values:
* true - OK
* false - bad name
*/
#include <config.h>
#ident "$Id$"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include "defines.h"
#include "chkname.h"
int allow_bad_names = false;
static bool is_valid_name (const char *name)
{
if (allow_bad_names) {
return true;
}
/*
* User/group names must match BRE regex:
* [a-zA-Z0-9_.][a-zA-Z0-9_.-]*$\?
*
* as a non-POSIX, extension, allow "$" as the last char for
* sake of Samba 3.x "add machine script"
*
* Also do not allow fully numeric names or just "." or "..".
*/
int numeric;
if ('\0' == *name ||
('.' == *name && (('.' == name[1] && '\0' == name[2]) ||
'\0' == name[1])) ||
!((*name >= 'a' && *name <= 'z') ||
(*name >= 'A' && *name <= 'Z') ||
(*name >= '0' && *name <= '9') ||
*name == '_' ||
*name == '.')) {
return false;
}
numeric = isdigit(*name);
while ('\0' != *++name) {
if (!((*name >= 'a' && *name <= 'z') ||
(*name >= 'A' && *name <= 'Z') ||
(*name >= '0' && *name <= '9') ||
*name == '_' ||
*name == '.' ||
*name == '-' ||
(*name == '$' && name[1] == '\0')
)) {
return false;
}
numeric &= isdigit(*name);
}
return !numeric;
}
bool
is_valid_user_name(const char *name)
{
long conf;
size_t maxsize;
errno = 0;
conf = sysconf(_SC_LOGIN_NAME_MAX);
if (conf == -1 && errno != 0)
maxsize = LOGIN_NAME_MAX;
else
maxsize = conf;
if (strlen(name) >= maxsize)
return false;
return is_valid_name(name);
}
bool is_valid_group_name (const char *name)
{
/*
* Arbitrary limit for group names.
* HP-UX 10 limits to 16 characters
*/
if ( (GROUP_NAME_MAX_LENGTH > 0)
&& (strlen (name) > GROUP_NAME_MAX_LENGTH)) {
return false;
}
return is_valid_name (name);
}
+27
View File
@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1997 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2005 , Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* $Id$ */
#ifndef _CHKNAME_H_
#define _CHKNAME_H_
/*
* is_valid_user_name(), is_valid_group_name() - check the new user/group
* name for validity;
* return values:
* true - OK
* false - bad name
*/
#include "defines.h"
extern bool is_valid_user_name (const char *name);
extern bool is_valid_group_name (const char *name);
#endif
+146
View File
@@ -0,0 +1,146 @@
/*
* SPDX-FileCopyrightText: 1992 - 1993, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2010 - , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <sys/types.h>
#include <sys/stat.h>
#include "prototypes.h"
#include "defines.h"
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
static int chown_tree_at (int at_fd,
const char *path,
uid_t old_uid,
uid_t new_uid,
gid_t old_gid,
gid_t new_gid)
{
DIR *dir;
const struct dirent *ent;
struct stat dir_sb;
int dir_fd, rc = 0;
dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (dir_fd < 0) {
return -1;
}
dir = fdopendir (dir_fd);
if (!dir) {
(void) close (dir_fd);
return -1;
}
/*
* Open the directory and read each entry. Every entry is tested
* to see if it is a directory, and if so this routine is called
* recursively. If not, it is checked to see if an ownership
* shall be changed.
*/
while ((ent = readdir (dir))) {
uid_t tmpuid = (uid_t) -1;
gid_t tmpgid = (gid_t) -1;
struct stat ent_sb;
/*
* Skip the "." and ".." entries
*/
if ( (strcmp (ent->d_name, ".") == 0)
|| (strcmp (ent->d_name, "..") == 0)) {
continue;
}
rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW);
if (rc < 0) {
break;
}
if (S_ISDIR (ent_sb.st_mode)) {
/*
* Do the entire subdirectory.
*/
rc = chown_tree_at (dirfd(dir), ent->d_name, old_uid, new_uid, old_gid, new_gid);
if (0 != rc) {
break;
}
}
/*
* By default, the IDs are not changed (-1).
*
* If the file is not owned by the user, the owner is not
* changed.
*
* If the file is not group-owned by the group, the
* group-owner is not changed.
*/
if (((uid_t) -1 == old_uid) || (ent_sb.st_uid == old_uid)) {
tmpuid = new_uid;
}
if (((gid_t) -1 == old_gid) || (ent_sb.st_gid == old_gid)) {
tmpgid = new_gid;
}
if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
rc = fchownat (dirfd(dir), ent->d_name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW);
if (0 != rc) {
break;
}
}
}
/*
* Now do the root of the tree
*/
if ((0 == rc) && (fstat (dirfd(dir), &dir_sb) == 0)) {
uid_t tmpuid = (uid_t) -1;
gid_t tmpgid = (gid_t) -1;
if (((uid_t) -1 == old_uid) || (dir_sb.st_uid == old_uid)) {
tmpuid = new_uid;
}
if (((gid_t) -1 == old_gid) || (dir_sb.st_gid == old_gid)) {
tmpgid = new_gid;
}
if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
rc = fchown (dirfd(dir), tmpuid, tmpgid);
}
} else {
rc = -1;
}
(void) closedir (dir);
return rc;
}
/*
* chown_tree - change ownership of files in a directory tree
*
* chown_dir() walks a directory tree and changes the ownership
* of all files owned by the provided user ID.
*
* Only files owned (resp. group-owned) by old_uid (resp. by old_gid)
* will have their ownership (resp. group-ownership) modified, unless
* old_uid (resp. old_gid) is set to -1.
*
* new_uid and new_gid can be set to -1 to indicate that no owner or
* group-owner shall be changed.
*/
int chown_tree (const char *root,
uid_t old_uid,
uid_t new_uid,
gid_t old_gid,
gid_t new_gid)
{
return chown_tree_at (AT_FDCWD, root, old_uid, new_uid, old_gid, new_gid);
}
+79
View File
@@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 2001, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <grp.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
#include "getdef.h"
#include "shadowlog.h"
/*
* chown_tty() sets the login tty to be owned by the new user ID
* with TTYPERM modes
*/
void chown_tty (const struct passwd *info)
{
struct group *grent;
gid_t gid;
/*
* See if login.defs has some value configured for the port group
* ID. Otherwise, use the user's primary group ID.
*/
grent = getgr_nam_gid (getdef_str ("TTYGROUP"));
if (NULL != grent) {
gid = grent->gr_gid;
gr_free (grent);
} else {
gid = info->pw_gid;
}
/*
* Change the permissions on the TTY to be owned by the user with
* the group as determined above.
*/
if ( (fchown (STDIN_FILENO, info->pw_uid, gid) != 0)
|| (fchmod (STDIN_FILENO, getdef_num ("TTYPERM", 0600)) != 0)) {
int err = errno;
FILE *shadow_logfd = log_get_logfd();
fprintf (shadow_logfd,
_("Unable to change owner or mode of tty stdin: %s"),
strerror (err));
SYSLOG ((LOG_WARN,
"unable to change owner or mode of tty stdin for user `%s': %s\n",
info->pw_name, strerror (err)));
if (EROFS != err) {
closelog ();
exit (EXIT_FAILURE);
}
}
#ifdef __linux__
/*
* Please don't add code to chown /dev/vcs* to the user logging in -
* it's a potential security hole. I wouldn't like the previous user
* to hold the file descriptor open and watch my screen. We don't
* have the *BSD revoke() system call yet, and vhangup() only works
* for tty devices (which vcs* is not). --marekm
*/
#endif
}
+121
View File
@@ -0,0 +1,121 @@
/*
* SPDX-FileCopyrightText: 2008 - 2011, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <assert.h>
#include <stdio.h>
#include "prototypes.h"
/*
* The cleanup_functions stack.
*/
#define CLEANUP_FUNCTIONS 10
typedef /*@null@*/void * parg_t;
static cleanup_function cleanup_functions[CLEANUP_FUNCTIONS];
static parg_t cleanup_function_args[CLEANUP_FUNCTIONS];
static pid_t cleanup_pid = 0;
/*
* - Cleanup functions shall not fail.
* - You should register do_cleanups with atexit.
* - You should add cleanup functions to the stack with add_cleanup when
* an operation is expected to be executed later, and remove it from the
* stack with del_cleanup when it has been executed.
*
**/
/*
* do_cleanups - perform the actions stored in the cleanup_functions stack.
*
* Cleanup action are not executed on exit of the processes started by the
* parent (first caller of add_cleanup).
*
* It is intended to be used as:
* atexit (do_cleanups);
*/
void do_cleanups (void)
{
unsigned int i;
/* Make sure there were no overflow */
assert (NULL == cleanup_functions[CLEANUP_FUNCTIONS-1]);
if (getpid () != cleanup_pid) {
return;
}
i = CLEANUP_FUNCTIONS;
do {
i--;
if (cleanup_functions[i] != NULL) {
cleanup_functions[i] (cleanup_function_args[i]);
}
} while (i>0);
}
/*
* add_cleanup - Add a cleanup_function to the cleanup_functions stack.
*/
void add_cleanup (/*@notnull@*/cleanup_function pcf, /*@null@*/void *arg)
{
unsigned int i;
assert (NULL != pcf);
assert (NULL == cleanup_functions[CLEANUP_FUNCTIONS-2]);
if (0 == cleanup_pid) {
cleanup_pid = getpid ();
}
/* Add the cleanup_function at the end of the stack */
for (i=0; NULL != cleanup_functions[i]; i++);
cleanup_functions[i] = pcf;
cleanup_function_args[i] = arg;
}
/*
* del_cleanup - Remove a cleanup_function from the cleanup_functions stack.
*/
void del_cleanup (/*@notnull@*/cleanup_function pcf)
{
unsigned int i;
assert (NULL != pcf);
/* Find the pcf cleanup function */
for (i=0; i<CLEANUP_FUNCTIONS; i++) {
if (cleanup_functions[i] == pcf) {
break;
}
}
/* Make sure the cleanup function was found */
assert (i<CLEANUP_FUNCTIONS);
/* Move the rest of the cleanup functions */
for (; i<CLEANUP_FUNCTIONS; i++) {
/* Make sure the cleanup function was specified only once */
assert ( (i == (CLEANUP_FUNCTIONS -1))
|| (cleanup_functions[i+1] != pcf));
if (i == (CLEANUP_FUNCTIONS -1)) {
cleanup_functions[i] = NULL;
cleanup_function_args[i] = NULL;
} else {
cleanup_functions[i] = cleanup_functions[i+1];
cleanup_function_args[i] = cleanup_function_args[i+1];
}
/* A NULL indicates the end of the stack */
if (NULL == cleanup_functions[i]) {
break;
}
}
}
+216
View File
@@ -0,0 +1,216 @@
/*
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <assert.h>
#include <stdio.h>
#include "attr.h"
#include "defines.h"
#include "groupio.h"
#include "sgroupio.h"
#include "prototypes.h"
#include "shadowlog.h"
/*
* cleanup_report_add_group - Report failure to add a group to the system
*
* It should be registered when it is decided to add a group to the system.
*/
void cleanup_report_add_group (void *group_name)
{
const char *name = group_name;
SYSLOG ((LOG_ERR, "failed to add group %s", name));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_GROUP, log_get_progname(),
"",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
/*
* cleanup_report_del_group - Report failure to remove a group from the system
*
* It should be registered when it is decided to remove a group from the system.
*/
void cleanup_report_del_group (void *group_name)
{
const char *name = group_name;
SYSLOG ((LOG_ERR, "failed to remove group %s", name));
#ifdef WITH_AUDIT
audit_logger (AUDIT_DEL_GROUP, log_get_progname(),
"",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
void cleanup_report_mod_group (void *cleanup_info)
{
const struct cleanup_info_mod *info;
info = (const struct cleanup_info_mod *)cleanup_info;
SYSLOG ((LOG_ERR,
"failed to change %s (%s)",
gr_dbname (),
info->action));
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_ACCT, log_get_progname(),
info->audit_msg,
info->name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
#ifdef SHADOWGRP
void cleanup_report_mod_gshadow (void *cleanup_info)
{
const struct cleanup_info_mod *info;
info = (const struct cleanup_info_mod *)cleanup_info;
SYSLOG ((LOG_ERR,
"failed to change %s (%s)",
sgr_dbname (),
info->action));
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_ACCT, log_get_progname(),
info->audit_msg,
info->name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
#endif
/*
* cleanup_report_add_group_group - Report failure to add a group to group
*
* It should be registered when it is decided to add a group to the
* group database.
*/
void cleanup_report_add_group_group (void *group_name)
{
const char *name = group_name;
SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, gr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_GROUP, log_get_progname(),
"adding group to /etc/group",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
#ifdef SHADOWGRP
/*
* cleanup_report_add_group_gshadow - Report failure to add a group to gshadow
*
* It should be registered when it is decided to add a group to the
* gshadow database.
*/
void cleanup_report_add_group_gshadow (void *group_name)
{
const char *name = group_name;
SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, sgr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_GROUP, log_get_progname(),
"adding group to /etc/gshadow",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
#endif
/*
* cleanup_report_del_group_group - Report failure to remove a group from the
* regular group database
*
* It should be registered when it is decided to remove a group from the
* regular group database.
*/
void cleanup_report_del_group_group (void *group_name)
{
const char *name = group_name;
SYSLOG ((LOG_ERR,
"failed to remove group %s from %s",
name, gr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_GROUP, log_get_progname(),
"removing group from /etc/group",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
#ifdef SHADOWGRP
/*
* cleanup_report_del_group_gshadow - Report failure to remove a group from
* gshadow
*
* It should be registered when it is decided to remove a group from the
* gshadow database.
*/
void cleanup_report_del_group_gshadow (void *group_name)
{
const char *name = group_name;
SYSLOG ((LOG_ERR,
"failed to remove group %s from %s",
name, sgr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_GROUP, log_get_progname(),
"removing group from /etc/gshadow",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
#endif
/*
* cleanup_unlock_group - Unlock the group file
*
* It should be registered after the group file is successfully locked.
*/
void cleanup_unlock_group (MAYBE_UNUSED void *arg)
{
if (gr_unlock () == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), gr_dbname ());
SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
#ifdef WITH_AUDIT
audit_logger_message ("unlocking group file",
SHADOW_AUDIT_FAILURE);
#endif
}
}
#ifdef SHADOWGRP
/*
* cleanup_unlock_gshadow - Unlock the gshadow file
*
* It should be registered after the gshadow file is successfully locked.
*/
void cleanup_unlock_gshadow (MAYBE_UNUSED void *arg)
{
if (sgr_unlock () == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), sgr_dbname ());
SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
#ifdef WITH_AUDIT
audit_logger_message ("unlocking gshadow file",
SHADOW_AUDIT_FAILURE);
#endif
}
}
#endif
+131
View File
@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <assert.h>
#include <stdio.h>
#include "attr.h"
#include "defines.h"
#include "pwio.h"
#include "shadowio.h"
#include "prototypes.h"
#include "shadowlog.h"
/*
* cleanup_report_add_user - Report failure to add a user to the system
*
* It should be registered when it is decided to add a user to the system.
*/
void cleanup_report_add_user (void *user_name)
{
const char *name = user_name;
SYSLOG ((LOG_ERR, "failed to add user %s", name));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_USER, log_get_progname(),
"",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
void cleanup_report_mod_passwd (void *cleanup_info)
{
const struct cleanup_info_mod *info;
info = (const struct cleanup_info_mod *)cleanup_info;
SYSLOG ((LOG_ERR,
"failed to change %s (%s)",
pw_dbname (),
info->action));
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_ACCT, log_get_progname(),
info->audit_msg,
info->name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
/*
* cleanup_report_add_user_passwd - Report failure to add a user to
* /etc/passwd
*
* It should be registered when it is decided to add a user to the
* /etc/passwd database.
*/
void cleanup_report_add_user_passwd (void *user_name)
{
const char *name = user_name;
SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, pw_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_USER, log_get_progname(),
"adding user to /etc/passwd",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
/*
* cleanup_report_add_user_shadow - Report failure to add a user to
* /etc/shadow
*
* It should be registered when it is decided to add a user to the
* /etc/shadow database.
*/
void cleanup_report_add_user_shadow (void *user_name)
{
const char *name = user_name;
SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, spw_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_USER, log_get_progname(),
"adding user to /etc/shadow",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
#endif
}
/*
* cleanup_unlock_passwd - Unlock the /etc/passwd database
*
* It should be registered after the passwd database is successfully locked.
*/
void cleanup_unlock_passwd (MAYBE_UNUSED void *arg)
{
if (pw_unlock () == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), pw_dbname ());
SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
#ifdef WITH_AUDIT
audit_logger_message ("unlocking passwd file",
SHADOW_AUDIT_FAILURE);
#endif
}
}
/*
* cleanup_unlock_shadow - Unlock the /etc/shadow database
*
* It should be registered after the shadow database is successfully locked.
*/
void cleanup_unlock_shadow (MAYBE_UNUSED void *arg)
{
if (spw_unlock () == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), spw_dbname ());
SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
#ifdef WITH_AUDIT
audit_logger_message ("unlocking shadow file",
SHADOW_AUDIT_FAILURE);
#endif
}
}
+54 -67
View File
@@ -21,6 +21,9 @@
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include "alloc.h"
#include "memzero.h"
#include "nscd.h"
#include "sssd.h"
#ifdef WITH_TCB
@@ -29,6 +32,8 @@
#include "prototypes.h"
#include "commonio.h"
#include "shadowlog_internal.h"
#include "string/sprintf.h"
/* local function prototypes */
static int lrename (const char *, const char *);
@@ -106,9 +111,9 @@ static int check_link_count (const char *file, bool log)
if (sb.st_nlink != 2) {
if (log) {
(void) fprintf (shadow_logfd,
"%s: %s: lock file already used (nlink: %u)\n",
shadow_progname, file, sb.st_nlink);
fprintf(shadow_logfd,
"%s: %s: lock file already used (nlink: %ju)\n",
shadow_progname, file, (uintmax_t) sb.st_nlink);
}
return 0;
}
@@ -119,11 +124,11 @@ static int check_link_count (const char *file, bool log)
static int do_lock_file (const char *file, const char *lock, bool log)
{
int fd;
pid_t pid;
ssize_t len;
int retval;
char buf[32];
int fd;
int retval;
char buf[32];
pid_t pid;
ssize_t len;
fd = open (file, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (-1 == fd) {
@@ -136,9 +141,9 @@ static int do_lock_file (const char *file, const char *lock, bool log)
}
pid = getpid ();
snprintf (buf, sizeof buf, "%lu", (unsigned long) pid);
SNPRINTF(buf, "%lu", (unsigned long) pid);
len = (ssize_t) strlen (buf) + 1;
if (write (fd, buf, (size_t) len) != len) {
if (write_full(fd, buf, len) == -1) {
if (log) {
(void) fprintf (shadow_logfd,
"%s: %s file write error: %s\n",
@@ -190,7 +195,7 @@ static int do_lock_file (const char *file, const char *lock, bool log)
return 0;
}
buf[len] = '\0';
if (get_pid (buf, &pid) == 0) {
if (get_pid(buf, &pid) == -1) {
if (log) {
(void) fprintf (shadow_logfd,
"%s: existing lock file %s with an invalid PID '%s'\n",
@@ -251,25 +256,13 @@ static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
return NULL;
}
#ifdef HAVE_FCHOWN
if (fchown (fileno (fp), sb->st_uid, sb->st_gid) != 0) {
goto fail;
}
#else /* !HAVE_FCHOWN */
if (chown (name, sb->st_mode) != 0) {
goto fail;
}
#endif /* !HAVE_FCHOWN */
#ifdef HAVE_FCHMOD
if (fchmod (fileno (fp), sb->st_mode & 0664) != 0) {
goto fail;
}
#else /* !HAVE_FCHMOD */
if (chmod (name, sb->st_mode & 0664) != 0) {
goto fail;
}
#endif /* !HAVE_FCHMOD */
return fp;
fail:
@@ -349,7 +342,7 @@ static void free_linked_list (struct commonio_db *db)
int commonio_setname (struct commonio_db *db, const char *name)
{
snprintf (db->filename, sizeof (db->filename), "%s", name);
SNPRINTF(db->filename, "%s", name);
db->setname = true;
return 1;
}
@@ -363,33 +356,25 @@ bool commonio_present (const struct commonio_db *db)
int commonio_lock_nowait (struct commonio_db *db, bool log)
{
char* file = NULL;
char* lock = NULL;
size_t lock_file_len;
size_t file_len;
int err = 0;
int err = 0;
char *file = NULL;
char *lock = NULL;
if (db->locked) {
return 1;
}
file_len = strlen(db->filename) + 11;/* %lu max size */
lock_file_len = strlen(db->filename) + 6; /* sizeof ".lock" */
file = (char*)malloc(file_len);
if (file == NULL) {
if (asprintf(&file, "%s.%ju", db->filename, (uintmax_t) getpid()) == -1)
goto cleanup_ENOMEM;
}
lock = (char*)malloc(lock_file_len);
if (lock == NULL) {
if (asprintf(&lock, "%s.lock", db->filename) == -1)
goto cleanup_ENOMEM;
}
snprintf (file, file_len, "%s.%lu",
db->filename, (unsigned long) getpid ());
snprintf (lock, lock_file_len, "%s.lock", db->filename);
if (do_lock_file (file, lock, log) != 0) {
db->locked = true;
lock_count++;
err = 1;
}
cleanup_ENOMEM:
free(file);
free(lock);
@@ -483,7 +468,7 @@ static void dec_lock_count (void)
int commonio_unlock (struct commonio_db *db)
{
char lock[1024];
char lock[1029];
if (db->isopen) {
db->readonly = true;
@@ -500,7 +485,7 @@ int commonio_unlock (struct commonio_db *db)
* then call ulckpwdf() (if used) on last unlock.
*/
db->locked = false;
snprintf (lock, sizeof lock, "%s.lock", db->filename);
SNPRINTF(lock, "%s.lock", db->filename);
unlink (lock);
dec_lock_count ();
return 1;
@@ -618,7 +603,7 @@ int commonio_open (struct commonio_db *db, int mode)
fd = open (db->filename,
(db->readonly ? O_RDONLY : O_RDWR)
| O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
| O_NOCTTY | O_NONBLOCK | O_NOFOLLOW | O_CLOEXEC);
saved_errno = errno;
db->fp = NULL;
if (fd >= 0) {
@@ -649,22 +634,19 @@ int commonio_open (struct commonio_db *db, int mode)
return 0;
}
/* Do not inherit fd in spawned processes (e.g. nscd) */
fcntl (fileno (db->fp), F_SETFD, FD_CLOEXEC);
buflen = BUFLEN;
buf = (char *) malloc (buflen);
buf = MALLOC(buflen, char);
if (NULL == buf) {
goto cleanup_ENOMEM;
}
while (db->ops->fgets (buf, (int) buflen, db->fp) == buf) {
while ( ((cp = strrchr (buf, '\n')) == NULL)
while (db->ops->fgets (buf, buflen, db->fp) == buf) {
while ( (strrchr (buf, '\n') == NULL)
&& (feof (db->fp) == 0)) {
size_t len;
buflen += BUFLEN;
cp = (char *) realloc (buf, buflen);
cp = REALLOC(buf, buflen, char);
if (NULL == cp) {
goto cleanup_buf;
}
@@ -698,7 +680,7 @@ int commonio_open (struct commonio_db *db, int mode)
}
}
p = (struct commonio_entry *) malloc (sizeof *p);
p = MALLOC(1, struct commonio_entry);
if (NULL == p) {
goto cleanup_entry;
}
@@ -775,7 +757,7 @@ commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *))
return 0;
}
entries = malloc (n * sizeof (struct commonio_entry *));
entries = MALLOC(n, struct commonio_entry *);
if (entries == NULL) {
return -1;
}
@@ -912,9 +894,9 @@ static int write_all (const struct commonio_db *db)
int commonio_close (struct commonio_db *db)
{
char buf[1024];
int errors = 0;
struct stat sb;
int errors = 0;
char buf[1024];
struct stat sb;
if (!db->isopen) {
errno = EINVAL;
@@ -945,7 +927,11 @@ int commonio_close (struct commonio_db *db)
/*
* Create backup file.
*/
snprintf (buf, sizeof buf, "%s-", db->filename);
if (SNPRINTF(buf, "%s-", db->filename) == -1) {
(void) fclose (db->fp);
db->fp = NULL;
goto fail;
}
#ifdef WITH_SELINUX
if (set_selinux_file_context (db->filename, S_IFREG) != 0) {
@@ -960,15 +946,15 @@ int commonio_close (struct commonio_db *db)
errors++;
}
db->fp = NULL;
#ifdef WITH_SELINUX
if (reset_selinux_file_context () != 0) {
errors++;
}
#endif
if (errors != 0) {
db->fp = NULL;
if (errors != 0)
goto fail;
}
} else {
/*
* Default permissions for new [g]shadow files.
@@ -978,7 +964,8 @@ int commonio_close (struct commonio_db *db)
sb.st_gid = db->st_gid;
}
snprintf (buf, sizeof buf, "%s+", db->filename);
if (SNPRINTF(buf, "%s+", db->filename) == -1)
goto fail;
#ifdef WITH_SELINUX
if (set_selinux_file_context (db->filename, S_IFREG) != 0) {
@@ -998,13 +985,11 @@ int commonio_close (struct commonio_db *db)
if (fflush (db->fp) != 0) {
errors++;
}
#ifdef HAVE_FSYNC
if (fsync (fileno (db->fp)) != 0) {
errors++;
}
#else /* !HAVE_FSYNC */
sync ();
#endif /* !HAVE_FSYNC */
if (fclose (db->fp) != 0) {
errors++;
}
@@ -1096,7 +1081,7 @@ int commonio_update (struct commonio_db *db, const void *eptr)
return 1;
}
/* not found, new entry */
p = (struct commonio_entry *) malloc (sizeof *p);
p = MALLOC(1, struct commonio_entry);
if (NULL == p) {
db->ops->free (nentry);
errno = ENOMEM;
@@ -1133,7 +1118,7 @@ int commonio_append (struct commonio_db *db, const void *eptr)
return 0;
}
/* new entry */
p = (struct commonio_entry *) malloc (sizeof *p);
p = MALLOC(1, struct commonio_entry);
if (NULL == p) {
db->ops->free (nentry);
errno = ENOMEM;
@@ -1200,6 +1185,8 @@ int commonio_remove (struct commonio_db *db, const char *name)
db->ops->free (p->eptr);
}
free(p);
return 1;
}
+8 -2
View File
@@ -11,8 +11,11 @@
#ifndef COMMONIO_H
#define COMMONIO_H
#include "attr.h"
#include "defines.h" /* bool */
/*
* Linked list entry.
*/
@@ -37,7 +40,7 @@ struct commonio_ops {
/*
* free() the object including any strings pointed by it.
*/
void (*free) (/*@out@*/ /*@only@*/void *);
void (*free)(/*@only@*/void *);
/*
* Return the name of the object (for example, pw_name
@@ -61,7 +64,9 @@ struct commonio_ops {
* fgets and fputs (can be replaced by versions that
* understand line continuation conventions).
*/
/*@null@*/char *(*fgets) (/*@returned@*/ /*@out@*/char *s, int n, FILE *stream);
ATTR_ACCESS(write_only, 1, 2)
/*@null@*/char *(*fgets)(/*@returned@*/char *restrict s, int n,
FILE *restrict stream);
int (*fputs) (const char *, FILE *);
/*
@@ -123,6 +128,7 @@ extern int commonio_setname (struct commonio_db *, const char *);
extern bool commonio_present (const struct commonio_db *db);
extern int commonio_lock (struct commonio_db *);
extern int commonio_lock_nowait (struct commonio_db *, bool log);
extern int do_fcntl_lock (const char *file, bool log, short type);
extern int commonio_open (struct commonio_db *, int);
extern /*@observer@*/ /*@null@*/const void *commonio_locate (struct commonio_db *, const char *);
extern int commonio_update (struct commonio_db *, const void *);
+109
View File
@@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 1991 , Julianne Frances Haugh
* SPDX-FileCopyrightText: 1991 , Chip Rosenthal
* SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "defines.h"
#include <stdio.h>
#include "getdef.h"
#include "prototypes.h"
#include "string/strtcpy.h"
#ident "$Id$"
/*
* This is now rather generic function which decides if "tty" is listed
* under "cfgin" in config (directly or indirectly). Fallback to default if
* something is bad.
*/
static bool is_listed (const char *cfgin, const char *tty, bool def)
{
FILE *fp;
char buf[1024], *s;
const char *cons;
/*
* If the CONSOLE configuration definition isn't given,
* fallback to default.
*/
cons = getdef_str (cfgin);
if (NULL == cons) {
return def;
}
/*
* If this isn't a filename, then it is a ":" delimited list of
* console devices upon which root logins are allowed.
*/
if (*cons != '/') {
char *pbuf;
STRTCPY(buf, cons);
pbuf = &buf[0];
while ((s = strtok (pbuf, ":")) != NULL) {
if (strcmp (s, tty) == 0) {
return true;
}
pbuf = NULL;
}
return false;
}
/*
* If we can't open the console list, then call everything a
* console - otherwise root will never be allowed to login.
*/
fp = fopen (cons, "r");
if (NULL == fp) {
return def;
}
/*
* See if this tty is listed in the console file.
*/
while (fgets (buf, sizeof (buf), fp) != NULL) {
/* Remove optional trailing '\n'. */
buf[strcspn (buf, "\n")] = '\0';
if (strcmp (buf, tty) == 0) {
(void) fclose (fp);
return true;
}
}
/*
* This tty isn't a console.
*/
(void) fclose (fp);
return false;
}
/*
* console - return 1 if the "tty" is a console device, else 0.
*
* Note - we need to take extreme care here to avoid locking out root logins
* if something goes awry. That's why we do things like call everything a
* console if the consoles file can't be opened. Because of this, we must
* warn the user to protect against the remove of the consoles file since
* that would allow an unauthorized root login.
*/
bool console (const char *tty)
{
if (strncmp (tty, "/dev/", 5) == 0) {
tty += 5;
}
return is_listed ("CONSOLE", tty, true);
}
+925
View File
@@ -0,0 +1,925 @@
/*
* SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 2001, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include "alloc.h"
#include "attr.h"
#include "prototypes.h"
#include "defines.h"
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#endif /* WITH_SELINUX */
#if defined(WITH_ACL) || defined(WITH_ATTR)
#include <stdarg.h>
#include <attr/error_context.h>
#endif /* WITH_ACL || WITH_ATTR */
#ifdef WITH_ACL
#include <acl/libacl.h>
#endif /* WITH_ACL */
#ifdef WITH_ATTR
#include <attr/libattr.h>
#endif /* WITH_ATTR */
#include "shadowlog.h"
#include "string/sprintf.h"
static /*@null@*/const char *src_orig;
static /*@null@*/const char *dst_orig;
struct link_name {
dev_t ln_dev;
ino_t ln_ino;
nlink_t ln_count;
char *ln_name;
/*@dependent@*/struct link_name *ln_next;
};
static /*@exposed@*/struct link_name *links;
struct path_info {
const char *full_path;
int dirfd;
const char *name;
};
static int copy_entry (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int copy_dir (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static /*@null@*/char *readlink_malloc (const char *filename);
static int copy_symlink (const struct path_info *src, const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int copy_hardlink (const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
struct link_name *lp);
static int copy_special (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int copy_file (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int chownat_if_needed (const struct path_info *dst, const struct stat *statp,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int fchown_if_needed (int fdst, const struct stat *statp,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
#if defined(WITH_ACL) || defined(WITH_ATTR)
/*
* error_acl - format the error messages for the ACL and EQ libraries.
*/
format_attr(printf, 2, 3)
static void error_acl (MAYBE_UNUSED struct error_context *ctx, const char *fmt, ...)
{
va_list ap;
FILE *shadow_logfd = log_get_logfd();
/* ignore the case when destination does not support ACLs
* or extended attributes */
if (ENOTSUP == errno) {
errno = 0;
return;
}
va_start (ap, fmt);
(void) fprintf (shadow_logfd, _("%s: "), log_get_progname());
if (vfprintf (shadow_logfd, fmt, ap) != 0) {
(void) fputs (_(": "), shadow_logfd);
}
(void) fprintf (shadow_logfd, "%s\n", strerror (errno));
va_end (ap);
}
static struct error_context ctx = {
error_acl, NULL, NULL
};
#endif /* WITH_ACL || WITH_ATTR */
#ifdef WITH_ACL
static int perm_copy_path(const struct path_info *src,
const struct path_info *dst,
struct error_context *errctx)
{
int src_fd, dst_fd, ret;
src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC);
if (src_fd < 0) {
return -1;
}
dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC);
if (dst_fd < 0) {
(void) close (src_fd);
return -1;
}
ret = perm_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, errctx);
(void) close (src_fd);
(void) close (dst_fd);
return ret;
}
#endif /* WITH_ACL */
#ifdef WITH_ATTR
static int attr_copy_path(const struct path_info *src,
const struct path_info *dst,
int (*callback) (const char *, struct error_context *),
struct error_context *errctx)
{
int src_fd, dst_fd, ret;
src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC);
if (src_fd < 0) {
return -1;
}
dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC);
if (dst_fd < 0) {
(void) close (src_fd);
return -1;
}
ret = attr_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, callback, errctx);
(void) close (src_fd);
(void) close (dst_fd);
return ret;
}
#endif /* WITH_ATTR */
/*
* remove_link - delete a link from the linked list
*/
static void remove_link (/*@only@*/struct link_name *ln)
{
struct link_name *lp;
if (links == ln) {
links = ln->ln_next;
free (ln->ln_name);
free (ln);
return;
}
for (lp = links; NULL !=lp; lp = lp->ln_next) {
if (lp->ln_next == ln) {
break;
}
}
if (NULL == lp) {
free (ln->ln_name);
free (ln);
return;
}
lp->ln_next = lp->ln_next->ln_next;
free (ln->ln_name);
free (ln);
}
/*
* check_link - see if a file is really a link
*/
static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, const struct stat *sb)
{
struct link_name *lp;
/* copy_tree () must be the entry point */
assert (NULL != src_orig);
assert (NULL != dst_orig);
for (lp = links; NULL != lp; lp = lp->ln_next) {
if ((lp->ln_dev == sb->st_dev) && (lp->ln_ino == sb->st_ino)) {
return lp;
}
}
if (sb->st_nlink == 1) {
return NULL;
}
lp = XMALLOC(1, struct link_name);
lp->ln_dev = sb->st_dev;
lp->ln_ino = sb->st_ino;
lp->ln_count = sb->st_nlink;
xasprintf(&lp->ln_name, "%s%s", dst_orig, name + strlen(src_orig));
lp->ln_next = links;
links = lp;
return NULL;
}
static int copy_tree_impl (const struct path_info *src, const struct path_info *dst,
bool copy_root, bool reset_selinux,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
int dst_fd, src_fd, err = 0;
bool set_orig = false;
const struct dirent *ent;
DIR *dir;
if (copy_root) {
struct stat sb;
if ( fstatat (dst->dirfd, dst->name, &sb, 0) == 0
|| errno != ENOENT) {
return -1;
}
if (fstatat (src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
return -1;
}
if (!S_ISDIR (sb.st_mode)) {
fprintf (log_get_logfd(),
"%s: %s is not a directory",
log_get_progname(), src->full_path);
return -1;
}
return copy_entry (src, dst, reset_selinux,
old_uid, new_uid, old_gid, new_gid);
}
/*
* Make certain both directories exist. This routine is called
* after the home directory is created, or recursively after the
* target is created. It assumes the target directory exists.
*/
src_fd = openat (src->dirfd, src->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (src_fd < 0) {
return -1;
}
dst_fd = openat (dst->dirfd, dst->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (dst_fd < 0) {
(void) close (src_fd);
return -1;
}
/*
* Open the source directory and read each entry. Every file
* entry in the directory is copied with the UID and GID set
* to the provided values. As an added security feature only
* regular files (and directories ...) are copied, and no file
* is made set-ID.
*/
dir = fdopendir (src_fd);
if (NULL == dir) {
(void) close (src_fd);
(void) close (dst_fd);
return -1;
}
if (src_orig == NULL) {
src_orig = src->full_path;
dst_orig = dst->full_path;
set_orig = true;
}
while ((0 == err) && (ent = readdir (dir)) != NULL) {
char *src_name = NULL;
char *dst_name;
struct path_info src_entry, dst_entry;
/*
* Skip the "." and ".." entries
*/
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
{
continue;
}
if (asprintf(&src_name, "%s/%s", src->full_path, ent->d_name) == -1)
{
err = -1;
continue;
}
if (asprintf(&dst_name, "%s/%s", dst->full_path, ent->d_name) == -1)
{
err = -1;
goto skip;
}
src_entry.full_path = src_name;
src_entry.dirfd = dirfd(dir);
src_entry.name = ent->d_name;
dst_entry.full_path = dst_name;
dst_entry.dirfd = dst_fd;
dst_entry.name = ent->d_name;
err = copy_entry(&src_entry, &dst_entry, reset_selinux,
old_uid, new_uid, old_gid, new_gid);
free(dst_name);
skip:
free(src_name);
}
(void) closedir (dir);
(void) close (dst_fd);
if (set_orig) {
src_orig = NULL;
dst_orig = NULL;
/* FIXME: clean links
* Since there can be hardlinks elsewhere on the device,
* we cannot check that all the hardlinks were found:
assert (NULL == links);
*/
}
#ifdef WITH_SELINUX
/* Reset SELinux to create files with default contexts.
* Note that the context is only reset on exit of copy_tree (it is
* assumed that the program would quit without needing a restored
* context if copy_tree failed previously), and that copy_tree can
* be called recursively (hence the context is set on the
* sub-functions of copy_entry).
*/
if (reset_selinux_file_context () != 0) {
err = -1;
}
#endif /* WITH_SELINUX */
return err;
}
/*
* copy_entry - copy the entry of a directory
*
* Copy the entry src to dst.
* Depending on the type of entry, this function will forward the
* request to copy_dir(), copy_symlink(), copy_hardlink(),
* copy_special(), or copy_file().
*
* The access and modification time will not be modified.
*
* The permissions will be set to new_uid/new_gid.
*
* If new_uid (resp. new_gid) is equal to -1, the user (resp. group) will
* not be modified.
*
* Only the files owned (resp. group-owned) by old_uid (resp.
* old_gid) will be modified, unless old_uid (resp. old_gid) is set
* to -1.
*/
static int copy_entry (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
int err = 0;
struct stat sb;
struct stat tmp_sb;
struct link_name *lp;
struct timespec mt[2];
if (fstatat(src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
/* If we cannot stat the file, do not care. */
return 0;
}
mt[0].tv_sec = sb.st_atim.tv_sec;
mt[0].tv_nsec = sb.st_atim.tv_nsec;
mt[1].tv_sec = sb.st_mtim.tv_sec;
mt[1].tv_nsec = sb.st_mtim.tv_nsec;
if (S_ISDIR (sb.st_mode)) {
err = copy_dir (src, dst, reset_selinux, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
/*
* If the destination already exists do nothing.
* This is after the copy_dir above to still iterate into subdirectories.
*/
if (fstatat(dst->dirfd, dst->name, &tmp_sb, AT_SYMLINK_NOFOLLOW) != -1) {
return err;
}
/*
* Copy any symbolic links
*/
else if (S_ISLNK (sb.st_mode)) {
err = copy_symlink (src, dst, reset_selinux, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
/*
* See if this is a previously copied link
*/
else if ((lp = check_link (src->full_path, &sb)) != NULL) {
err = copy_hardlink (dst, reset_selinux, lp);
}
/*
* Deal with FIFOs and special files. The user really
* shouldn't have any of these, but it seems like it
* would be nice to copy everything ...
*/
else if (!S_ISREG (sb.st_mode)) {
err = copy_special (src, dst, reset_selinux, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
/*
* Create the new file and copy the contents. The new
* file will be owned by the provided UID and GID values.
*/
else {
err = copy_file (src, dst, reset_selinux, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
return err;
}
/*
* copy_dir - copy a directory
*
* Copy a directory (recursively) from src to dst.
*
* statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
* the access and modification and the access rights.
*
* Return 0 on success, -1 on error.
*/
static int copy_dir (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
int err = 0;
struct stat dst_sb;
/*
* Create a new target directory, make it owned by
* the user and then recursively copy that directory.
*/
#ifdef WITH_SELINUX
if (set_selinux_file_context (dst->full_path, S_IFDIR) != 0) {
return -1;
}
#endif /* WITH_SELINUX */
/*
* If the destination is already a directory, don't change it
* but copy into it (recursively).
*/
if (fstatat(dst->dirfd, dst->name, &dst_sb, AT_SYMLINK_NOFOLLOW) == 0 && S_ISDIR(dst_sb.st_mode)) {
return (copy_tree_impl (src, dst, false, reset_selinux,
old_uid, new_uid, old_gid, new_gid) != 0);
}
if ( (mkdirat (dst->dirfd, dst->name, 0700) != 0)
|| (chownat_if_needed (dst, statp,
old_uid, new_uid, old_gid, new_gid) != 0)
|| (fchmodat (dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) != 0)
#ifdef WITH_ACL
|| ( (perm_copy_path (src, dst, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ACL */
#ifdef WITH_ATTR
/*
* If the third parameter is NULL, all extended attributes
* except those that define Access Control Lists are copied.
* ACLs are excluded by default because copying them between
* file systems with and without ACL support needs some
* additional logic so that no unexpected permissions result.
*/
|| ( !reset_selinux
&& (attr_copy_path (src, dst, NULL, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ATTR */
|| (copy_tree_impl (src, dst, false, reset_selinux,
old_uid, new_uid, old_gid, new_gid) != 0)
|| (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) {
err = -1;
}
return err;
}
/*
* readlink_malloc - wrapper for readlink
*
* return NULL on error.
* The return string shall be freed by the caller.
*/
static /*@null@*/char *readlink_malloc (const char *filename)
{
size_t size = 1024;
while (true) {
ssize_t nchars;
char *buffer = MALLOC(size, char);
if (NULL == buffer) {
return NULL;
}
nchars = readlink (filename, buffer, size);
if (nchars < 0) {
free(buffer);
return NULL;
}
if ((size_t) nchars < size) { /* The buffer was large enough */
/* readlink does not nul-terminate */
buffer[nchars] = '\0';
return buffer;
}
/* Try again with a bigger buffer */
free (buffer);
size *= 2;
}
}
/*
* copy_symlink - copy a symlink
*
* Copy a symlink from src to dst.
*
* statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
* the access and modification and the access rights.
*
* Return 0 on success, -1 on error.
*/
static int copy_symlink (const struct path_info *src, const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
char *oldlink;
/* copy_tree () must be the entry point */
assert (NULL != src_orig);
assert (NULL != dst_orig);
/*
* Get the name of the file which the link points
* to. If that name begins with the original
* source directory name, that part of the link
* name will be replaced with the original
* destination directory name.
*/
oldlink = readlink_malloc (src->full_path);
if (NULL == oldlink) {
return -1;
}
/* If src was a link to an entry of the src_orig directory itself,
* create a link to the corresponding entry in the dst_orig
* directory.
*/
if (strncmp(oldlink, src_orig, strlen(src_orig)) == 0) {
char *dummy;
xasprintf(&dummy, "%s%s", dst_orig, oldlink + strlen(src_orig));
free(oldlink);
oldlink = dummy;
}
#ifdef WITH_SELINUX
if (set_selinux_file_context (dst->full_path, S_IFLNK) != 0) {
free (oldlink);
return -1;
}
#endif /* WITH_SELINUX */
if ( (symlinkat (oldlink, dst->dirfd, dst->name) != 0)
|| (chownat_if_needed (dst, statp,
old_uid, new_uid, old_gid, new_gid) != 0)) {
/* FIXME: there are no modes on symlinks, right?
* ACL could be copied, but this would be much more
* complex than calling perm_copy_file.
* Ditto for Extended Attributes.
* We currently only document that ACL and Extended
* Attributes are not copied.
*/
free (oldlink);
return -1;
}
free (oldlink);
if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) {
return -1;
}
return 0;
}
/*
* copy_hardlink - copy a hardlink
*
* Copy a hardlink from src to dst.
*
* Return 0 on success, -1 on error.
*/
static int copy_hardlink (const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
struct link_name *lp)
{
/* FIXME: selinux, ACL, Extended Attributes needed? */
if (linkat (AT_FDCWD, lp->ln_name, dst->dirfd, dst->name, 0) != 0) {
return -1;
}
/* If the file could be unlinked, decrement the links counter,
* and forget about this link if it was the last reference */
lp->ln_count--;
if (lp->ln_count <= 0) {
remove_link (lp);
}
return 0;
}
/*
* copy_special - copy a special file
*
* Copy a special file from src to dst.
*
* statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
* the access and modification and the access rights.
*
* Return 0 on success, -1 on error.
*/
static int
copy_special(const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
#if defined(WITH_SELINUX)
if (set_selinux_file_context(dst->full_path, statp->st_mode & S_IFMT) != 0)
return -1;
#endif
if (mknodat(dst->dirfd, dst->name, statp->st_mode & ~07777U, statp->st_rdev) == -1)
return -1;
if (chownat_if_needed(dst, statp, old_uid, new_uid, old_gid, new_gid) == -1)
return -1;
if (fchmodat(dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) == -1)
return -1;
#if defined(WITH_ACL)
if (perm_copy_path(src, dst, &ctx) == -1 && errno != 0)
return -1;
#endif
#if defined(WITH_ATTR)
/*
* If the third parameter is NULL, all extended attributes
* except those that define Access Control Lists are copied.
* ACLs are excluded by default because copying them between
* file systems with and without ACL support needs some
* additional logic so that no unexpected permissions result.
*/
if (!reset_selinux) {
if (attr_copy_path(src, dst, NULL, &ctx) == -1 && errno != 0)
return -1;
}
#endif
if (utimensat(dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) == -1)
return -1;
return 0;
}
/*
* copy_file - copy a file
*
* Copy a file from src to dst.
*
* statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set
* the access and modification and the access rights.
*
* Return 0 on success, -1 on error.
*/
static int copy_file (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
int err = 0;
int ifd;
int ofd;
ifd = openat (src->dirfd, src->name, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
if (ifd < 0) {
return -1;
}
#ifdef WITH_SELINUX
if (set_selinux_file_context (dst->full_path, S_IFREG) != 0) {
(void) close (ifd);
return -1;
}
#endif /* WITH_SELINUX */
ofd = openat (dst->dirfd, dst->name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, 0600);
if ( (ofd < 0)
|| (fchown_if_needed (ofd, statp,
old_uid, new_uid, old_gid, new_gid) != 0)
|| (fchmod (ofd, statp->st_mode & 07777) != 0)
#ifdef WITH_ACL
|| ( (perm_copy_fd (src->full_path, ifd, dst->full_path, ofd, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ACL */
#ifdef WITH_ATTR
/*
* If the third parameter is NULL, all extended attributes
* except those that define Access Control Lists are copied.
* ACLs are excluded by default because copying them between
* file systems with and without ACL support needs some
* additional logic so that no unexpected permissions result.
*/
|| ( !reset_selinux
&& (attr_copy_fd (src->full_path, ifd, dst->full_path, ofd, NULL, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ATTR */
) {
if (ofd >= 0) {
(void) close (ofd);
}
(void) close (ifd);
return -1;
}
while (true) {
char buf[8192];
ssize_t cnt;
cnt = read (ifd, buf, sizeof buf);
if (cnt < 0) {
if (errno == EINTR) {
continue;
}
(void) close (ofd);
(void) close (ifd);
return -1;
}
if (cnt == 0) {
break;
}
if (write_full(ofd, buf, cnt) == -1) {
(void) close (ofd);
(void) close (ifd);
return -1;
}
}
(void) close (ifd);
if (close (ofd) != 0 && errno != EINTR) {
return -1;
}
if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) {
return -1;
}
return err;
}
#define def_chown_if_needed(chown_function, type_dst) \
static int chown_function ## _if_needed (type_dst dst, \
const struct stat *statp, \
uid_t old_uid, uid_t new_uid, \
gid_t old_gid, gid_t new_gid) \
{ \
uid_t tmpuid = (uid_t) -1; \
gid_t tmpgid = (gid_t) -1; \
\
/* Use new_uid if old_uid is set to -1 or if the file was \
* owned by the user. */ \
if (((uid_t) -1 == old_uid) || (statp->st_uid == old_uid)) { \
tmpuid = new_uid; \
} \
/* Otherwise, or if new_uid was set to -1, we keep the same \
* owner. */ \
if ((uid_t) -1 == tmpuid) { \
tmpuid = statp->st_uid; \
} \
\
if (((gid_t) -1 == old_gid) || (statp->st_gid == old_gid)) { \
tmpgid = new_gid; \
} \
if ((gid_t) -1 == tmpgid) { \
tmpgid = statp->st_gid; \
} \
\
return chown_function (dst, tmpuid, tmpgid); \
}
def_chown_if_needed (fchown, int)
static int chownat_if_needed (const struct path_info *dst,
const struct stat *statp,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
uid_t tmpuid = (uid_t) -1;
gid_t tmpgid = (gid_t) -1;
/* Use new_uid if old_uid is set to -1 or if the file was
* owned by the user. */
if (((uid_t) -1 == old_uid) || (statp->st_uid == old_uid)) {
tmpuid = new_uid;
}
/* Otherwise, or if new_uid was set to -1, we keep the same
* owner. */
if ((uid_t) -1 == tmpuid) {
tmpuid = statp->st_uid;
}
if (((gid_t) -1 == old_gid) || (statp->st_gid == old_gid)) {
tmpgid = new_gid;
}
if ((gid_t) -1 == tmpgid) {
tmpgid = statp->st_gid;
}
return fchownat (dst->dirfd, dst->name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW);
}
/*
* copy_tree - copy files in a directory tree
*
* copy_tree() walks a directory tree and copies ordinary files
* as it goes.
*
* When reset_selinux is enabled, extended attributes (and thus
* SELinux attributes) are not copied.
*
* old_uid and new_uid are used to set the ownership of the copied
* files. Unless old_uid is set to -1, only the files owned by
* old_uid have their ownership changed to new_uid. In addition, if
* new_uid is set to -1, no ownership will be changed.
*
* The same logic applies for the group-ownership and
* old_gid/new_gid.
*/
int copy_tree (const char *src_root, const char *dst_root,
bool copy_root, bool reset_selinux,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
const struct path_info src = {
.full_path = src_root,
.dirfd = AT_FDCWD,
.name = src_root
};
const struct path_info dst = {
.full_path = dst_root,
.dirfd = AT_FDCWD,
.name = dst_root
};
return copy_tree_impl(&src, &dst, copy_root, reset_selinux,
old_uid, new_uid, old_gid, new_gid);
}
+142
View File
@@ -0,0 +1,142 @@
/*
* SPDX-FileCopyrightText: Alejandro Colomar <alx@kernel.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#if HAVE_SYS_RANDOM_H
#include <sys/random.h>
#endif
#include "bit.h"
#include "defines.h"
#include "prototypes.h"
#include "shadowlog.h"
#include "sizeof.h"
static uint32_t csrand_uniform32(uint32_t n);
static unsigned long csrand_uniform_slow(unsigned long n);
/*
* Return a uniformly-distributed CS random u_long value.
*/
unsigned long
csrand(void)
{
FILE *fp;
unsigned long r;
#ifdef HAVE_GETENTROPY
/* getentropy may exist but lack kernel support. */
if (getentropy(&r, sizeof(r)) == 0)
return r;
#endif
#ifdef HAVE_GETRANDOM
/* Likewise getrandom. */
if (getrandom(&r, sizeof(r), 0) == sizeof(r))
return r;
#endif
#ifdef HAVE_ARC4RANDOM_BUF
/* arc4random_buf can never fail. */
arc4random_buf(&r, sizeof(r));
return r;
#endif
/* Use /dev/urandom as a last resort. */
fp = fopen("/dev/urandom", "r");
if (NULL == fp) {
goto fail;
}
if (fread(&r, sizeof(r), 1, fp) != 1) {
fclose(fp);
goto fail;
}
fclose(fp);
return r;
fail:
fprintf(log_get_logfd(), _("Unable to obtain random bytes.\n"));
exit(1);
}
/*
* Return a uniformly-distributed CS random value in the interval [0, n-1].
*/
unsigned long
csrand_uniform(unsigned long n)
{
if (n == 0 || n > UINT32_MAX)
return csrand_uniform_slow(n);
return csrand_uniform32(n);
}
/*
* Return a uniformly-distributed CS random value in the interval [min, max].
*/
unsigned long
csrand_interval(unsigned long min, unsigned long max)
{
return csrand_uniform(max - min + 1) + min;
}
/*
* Fast Random Integer Generation in an Interval
* ACM Transactions on Modeling and Computer Simulation 29 (1), 2019
* <https://arxiv.org/abs/1805.10941>
*/
static uint32_t
csrand_uniform32(uint32_t n)
{
uint32_t bound, rem;
uint64_t r, mult;
if (n == 0)
return csrand();
bound = -n % n; // analogous to `2^32 % n`, since `x % y == (x-y) % y`
do {
r = csrand();
mult = r * n;
rem = mult; // analogous to `mult % 2^32`
} while (rem < bound); // p = (2^32 % n) / 2^32; W.C.: n=2^31+1, p=0.5
r = mult >> WIDTHOF(n); // analogous to `mult / 2^32`
return r;
}
static unsigned long
csrand_uniform_slow(unsigned long n)
{
unsigned long r, max, mask;
max = n - 1;
mask = bit_ceil_wrapul(n) - 1;
do {
r = csrand();
r &= mask; // optimization
} while (r > max); // p = ((mask+1) % n) / (mask+1); W.C.: p=0.5
return r;
}
+30 -153
View File
@@ -6,41 +6,8 @@
#include "config.h"
#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
# if ! HAVE__BOOL
# ifdef __cplusplus
typedef bool _Bool;
# else
typedef unsigned char _Bool;
# endif
# endif
# define bool _Bool
# define false (0)
# define true (1)
# define __bool_true_false_are_defined 1
#endif
/* Take care of NLS matters. */
#ifdef S_SPLINT_S
extern char *setlocale(int categories, const char *locale);
# define LC_ALL (6)
extern char * bindtextdomain (const char * domainname, const char * dirname);
extern char * textdomain (const char * domainname);
# define _(Text) Text
# define ngettext(Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
#else
#ifdef HAVE_LOCALE_H
# include <locale.h>
#else
# undef setlocale
# define setlocale(category, locale) (NULL)
# ifndef LC_ALL
# define LC_ALL 6
# endif
#endif
#include <stdbool.h>
#include <locale.h>
#define gettext_noop(String) (String)
/* #define gettext_def(String) "#define String" */
@@ -57,22 +24,18 @@ extern char * textdomain (const char * domainname);
# define ngettext(Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_ERRNO_H
# include <errno.h>
#endif
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <unistd.h>
/*
* crypt(3), crypt_gensalt(3), and their
@@ -85,22 +48,6 @@ extern char * textdomain (const char * domainname);
#include <sys/time.h>
#include <time.h>
#ifdef HAVE_MEMSET_S
# define memzero(ptr, size) memset_s((ptr), 0, (size))
#elif defined HAVE_EXPLICIT_BZERO /* !HAVE_MEMSET_S */
# define memzero(ptr, size) explicit_bzero((ptr), (size))
#else /* !HAVE_MEMSET_S && HAVE_EXPLICIT_BZERO */
static inline void memzero(void *ptr, size_t size)
{
volatile unsigned char * volatile p = ptr;
while (size--) {
*p++ = '\0';
}
}
#endif /* !HAVE_MEMSET_S && !HAVE_EXPLICIT_BZERO */
#define strzero(s) memzero(s, strlen(s)) /* warning: evaluates twice */
#include <dirent.h>
/*
@@ -123,7 +70,6 @@ static inline void memzero(void *ptr, size_t size)
#endif
#endif
#ifdef USE_SYSLOG
#include <syslog.h>
#ifndef LOG_WARN
@@ -170,14 +116,6 @@ static inline void memzero(void *ptr, size_t size)
#define SYSLOG(x) syslog x
#endif /* !ENABLE_NLS */
#else /* !USE_SYSLOG */
#define SYSLOG(x) /* empty */
#define openlog(a,b,c) /* empty */
#define closelog() /* empty */
#endif /* !USE_SYSLOG */
/* The default syslog settings can now be changed here,
in just one place. */
@@ -192,63 +130,25 @@ static inline void memzero(void *ptr, size_t size)
#define OPENLOG(progname) openlog(progname, SYSLOG_OPTIONS, SYSLOG_FACILITY)
#ifndef F_OK
# define F_OK 0
# define X_OK 1
# define W_OK 2
# define R_OK 4
#endif
#ifndef SEEK_SET
# define SEEK_SET 0
# define SEEK_CUR 1
# define SEEK_END 2
#endif
#if HAVE_TERMIOS_H
# include <termios.h>
# define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio)
# define GTTY(fd, termio) tcgetattr(fd, termio)
# define TERMIO struct termios
# define USE_TERMIOS
#else /* assumed HAVE_TERMIO_H */
# include <sys/ioctl.h>
# include <termio.h>
# define STTY(fd, termio) ioctl(fd, TCSETA, termio)
# define GTTY(fd, termio) ioctl(fd, TCGETA, termio)
# define TEMRIO struct termio
# define USE_TERMIO
#endif
#include <termios.h>
#define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio)
#define GTTY(fd, termio) tcgetattr(fd, termio)
#define TERMIO struct termios
/*
* Password aging constants
*
* DAY - seconds / day
* WEEK - seconds / week
* SCALE - seconds / aging unit
*/
/* Solaris defines this in shadow.h */
#ifndef DAY
#define DAY (24L*3600L)
#define DAY ((time_t) 24 * 3600)
#endif
#define WEEK (7*DAY)
#ifdef ITI_AGING
#define SCALE 1
#else
#define SCALE DAY
#endif
/* Copy string pointed by B to array A with size checking. It was originally
in lmain.c but is _very_ useful elsewhere. Some setuid root programs with
very sloppy coding used to assume that BUFSIZ will always be enough... */
/* danger - side effects */
#define STRFCPY(A,B) \
(strncpy((A), (B), sizeof(A) - 1), (A)[sizeof(A) - 1] = '\0')
#ifndef PASSWD_FILE
#define PASSWD_FILE "/etc/passwd"
#endif
@@ -261,24 +161,20 @@ static inline void memzero(void *ptr, size_t size)
#define SHADOW_FILE "/etc/shadow"
#endif
#ifndef SUBUID_FILE
#define SUBUID_FILE "/etc/subuid"
#endif
#ifndef SUBGID_FILE
#define SUBGID_FILE "/etc/subgid"
#endif
#ifdef SHADOWGRP
#ifndef SGROUP_FILE
#define SGROUP_FILE "/etc/gshadow"
#endif
#endif
#ifndef NULL
#define NULL ((void *) 0)
#endif
#ifdef sun /* hacks for compiling on SunOS */
# ifndef SOLARIS
extern int fputs ();
extern char *strdup ();
extern char *strerror ();
# endif
#endif
/*
* string to use for the pw_passwd field in /etc/passwd when using
* shadow passwords - most systems use "x" but there are a few
@@ -288,45 +184,16 @@ extern char *strerror ();
#define SHADOW_PASSWD_STRING "x"
#endif
#define SHADOW_SP_FLAG_UNSET ((unsigned long int)-1)
#define SHADOW_SP_FLAG_UNSET ((unsigned long)-1)
#ifdef WITH_AUDIT
#ifdef __u8 /* in case we use pam < 0.80 */
/* in case we use pam < 0.80 */
#undef __u8
#endif
#ifdef __u32
#undef __u32
#endif
#include <libaudit.h>
#endif
/* To be used for verified unused parameters */
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
# define unused __attribute__((unused))
# define format_attr(type, index, check) __attribute__((format (type, index, check)))
#else
# define unused
# define format_attr(type, index, check)
#endif
/* Maximum length of usernames */
#ifdef HAVE_UTMPX_H
# include <utmpx.h>
# define USER_NAME_MAX_LENGTH (sizeof (((struct utmpx *)NULL)->ut_user))
#else
# include <utmp.h>
# ifdef HAVE_STRUCT_UTMP_UT_USER
# define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_user))
# else
# ifdef HAVE_STRUCT_UTMP_UT_NAME
# define USER_NAME_MAX_LENGTH (sizeof (((struct utmp *)NULL)->ut_name))
# else
# define USER_NAME_MAX_LENGTH 32
# endif
# endif
#endif
/* Maximum length of passwd entry */
#define PASSWD_ENTRY_MAX_LENGTH 32768
@@ -336,4 +203,14 @@ extern char *strerror ();
# define shadow_getenv(name) getenv(name)
#endif
/*
* Maximum password length
*
* Consider that there is also limit in PAM (PAM_MAX_RESP_SIZE)
* currently set to 512.
*/
#if !defined(PASS_MAX)
#define PASS_MAX BUFSIZ - 1
#endif
#endif /* _DEFINES_H_ */
+248
View File
@@ -0,0 +1,248 @@
/*
* SPDX-FileCopyrightText: 1989 - 1992, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alloc.h"
#include "prototypes.h"
#include "defines.h"
#include "shadowlog.h"
#include "string/sprintf.h"
/*
* NEWENVP_STEP must be a power of two. This is the number
* of (char *) pointers to allocate at a time, to avoid using
* realloc() too often.
*/
#define NEWENVP_STEP 16
size_t newenvc = 0;
/*@null@*/char **newenvp = NULL;
static const char *const forbid[] = {
"_RLD_=",
"BASH_ENV=", /* GNU creeping featurism strikes again... */
"ENV=",
"HOME=",
"IFS=",
"KRB_CONF=",
"LD_", /* anything with the LD_ prefix */
"LIBPATH=",
"MAIL=",
"NLSPATH=",
"PATH=",
"SHELL=",
"SHLIB_PATH=",
NULL
};
/* these are allowed, but with no slashes inside
(to work around security problems in GNU gettext) */
static const char *const noslash[] = {
"LANG=",
"LANGUAGE=",
"LC_", /* anything with the LC_ prefix */
NULL
};
/*
* initenv() must be called once before using addenv().
*/
void initenv (void)
{
newenvp = XMALLOC(NEWENVP_STEP, char *);
*newenvp = NULL;
}
void addenv (const char *string, /*@null@*/const char *value)
{
char *cp, *newstring;
size_t i, n;
if (NULL != value) {
xasprintf(&newstring, "%s=%s", string, value);
} else {
newstring = xstrdup (string);
}
/*
* Search for a '=' character within the string and if none is found
* just ignore the whole string.
*/
cp = strchr (newstring, '=');
if (NULL == cp) {
free(newstring);
return;
}
n = (size_t) (cp - newstring);
/*
* If this environment variable is already set, change its value.
*/
for (i = 0; i < newenvc; i++) {
if ( (strncmp (newstring, newenvp[i], n) == 0)
&& (('=' == newenvp[i][n]) || ('\0' == newenvp[i][n]))) {
break;
}
}
if (i < newenvc) {
free(newenvp[i]);
newenvp[i] = newstring;
return;
}
/*
* Otherwise, save the new environment variable
*/
newenvp[newenvc++] = newstring;
/*
* And extend the environment if needed.
*/
/*
* Check whether newenvc is a multiple of NEWENVP_STEP.
* If so we have to resize the vector.
* the expression (newenvc & (NEWENVP_STEP - 1)) == 0
* is equal to (newenvc % NEWENVP_STEP) == 0
* as long as NEWENVP_STEP is a power of 2.
*/
if ((newenvc & (NEWENVP_STEP - 1)) == 0) {
bool update_environ;
char **__newenvp;
/*
* If the resize operation succeeds we can
* happily go on, else print a message.
*/
update_environ = (environ == newenvp);
__newenvp = REALLOC(newenvp, newenvc + NEWENVP_STEP, char *);
if (NULL != __newenvp) {
/*
* If this is our current environment, update
* environ so that it doesn't point to some
* free memory area (realloc() could move it).
*/
if (update_environ)
environ = __newenvp;
newenvp = __newenvp;
} else {
(void) fputs (_("Environment overflow\n"), log_get_logfd());
newenvc--;
free (newenvp[newenvc]);
}
}
/*
* The last entry of newenvp must be NULL
*/
newenvp[newenvc] = NULL;
}
/*
* set_env - copy command line arguments into the environment
*/
void set_env (int argc, char *const *argv)
{
int noname = 1;
char variable[1024];
char *cp;
for (; argc > 0; argc--, argv++) {
if (strlen (*argv) >= sizeof variable) {
continue; /* ignore long entries */
}
cp = strchr (*argv, '=');
if (NULL == cp) {
assert(SNPRINTF(variable, "L%d", noname) != -1);
noname++;
addenv (variable, *argv);
} else {
const char *const *p;
for (p = forbid; NULL != *p; p++) {
if (strncmp (*argv, *p, strlen (*p)) == 0) {
break;
}
}
if (NULL != *p) {
stpcpy(mempcpy(variable, *argv, (size_t)(cp - *argv)), "");
printf (_("You may not change $%s\n"),
variable);
continue;
}
addenv (*argv, NULL);
}
}
}
/*
* sanitize_env - remove some nasty environment variables
* If you fall into a total paranoia, you should call this
* function for any root-setuid program or anything the user
* might change the environment with. 99% useless as almost
* all modern Unixes will handle setuid executables properly,
* but... I feel better with that silly precaution. -j.
*/
void sanitize_env (void)
{
char **envp = environ;
const char *const *bad;
char **cur;
char **move;
for (cur = envp; NULL != *cur; cur++) {
for (bad = forbid; NULL != *bad; bad++) {
if (strncmp (*cur, *bad, strlen (*bad)) == 0) {
for (move = cur; NULL != *move; move++) {
*move = *(move + 1);
}
cur--;
break;
}
}
}
for (cur = envp; NULL != *cur; cur++) {
for (bad = noslash; NULL != *bad; bad++) {
if (strncmp (*cur, *bad, strlen (*bad)) != 0) {
continue;
}
if (strchr (*cur, '/') == NULL) {
continue; /* OK */
}
for (move = cur; NULL != *move; move++) {
*move = *(move + 1);
}
cur--;
break;
}
}
}
+267
View File
@@ -0,0 +1,267 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2002 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "defines.h"
#include "faillog.h"
#include "failure.h"
#include "memzero.h"
#include "prototypes.h"
#include "string/strftime.h"
#include "string/strtcpy.h"
#define YEAR (365L*DAY)
/*
* failure - make failure entry
*
* failure() creates a new (struct faillog) entry or updates an
* existing one with the current failed login information.
*/
void failure (uid_t uid, const char *tty, struct faillog *fl)
{
int fd;
off_t offset_uid = (off_t) (sizeof *fl) * uid;
/*
* Don't do anything if failure logging isn't set up.
*/
if (access (FAILLOG_FILE, F_OK) != 0) {
return;
}
fd = open (FAILLOG_FILE, O_RDWR);
if (fd < 0) {
SYSLOG ((LOG_WARN,
"Can't write faillog entry for UID %lu in %s: %m",
(unsigned long) uid, FAILLOG_FILE));
return;
}
/*
* The file is indexed by UID value meaning that shared UID's
* share failure log records. That's OK since they really
* share just about everything else ...
*/
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (read (fd, fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
/* This is not necessarily a failure. The file is
* initially zero length.
*
* If lseek() or read() failed for any other reason, this
* might reset the counter. But the new failure will be
* logged.
*/
memzero (fl, sizeof *fl);
}
/*
* Update the record. We increment the failure count to log the
* latest failure. The only concern here is overflow, and we'll
* check for that. The line name and time of day are both
* updated as well.
*/
if (fl->fail_cnt + 1 > 0) {
fl->fail_cnt++;
}
STRTCPY(fl->fail_line, tty);
(void) time (&fl->fail_time);
/*
* Seek back to the correct position in the file and write the
* record out. Ideally we should lock the file in case the same
* account is being logged simultaneously. But the risk doesn't
* seem that great.
*/
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (write_full(fd, fl, sizeof *fl) == -1)) {
goto err_write;
}
if (close (fd) != 0 && errno != EINTR) {
goto err_close;
}
return;
err_write:
{
int saved_errno = errno;
(void) close (fd);
errno = saved_errno;
}
err_close:
SYSLOG ((LOG_WARN,
"Can't write faillog entry for UID %lu to %s: %m",
(unsigned long) uid, FAILLOG_FILE));
}
static bool too_many_failures (const struct faillog *fl)
{
time_t now;
if ((0 == fl->fail_max) || (fl->fail_cnt < fl->fail_max)) {
return false;
}
if (0 == fl->fail_locktime) {
return true; /* locked until reset manually */
}
(void) time (&now);
if ((fl->fail_time + fl->fail_locktime) < now) {
return false; /* enough time since last failure */
}
return true;
}
/*
* failcheck - check for failures > allowable
*
* failcheck() is called AFTER the password has been validated. If the
* account has been "attacked" with too many login failures, failcheck()
* returns 0 to indicate that the login should be denied even though
* the password is valid.
*
* failed indicates if the login failed AFTER the password has been
* validated.
*/
int failcheck (uid_t uid, struct faillog *fl, bool failed)
{
int fd;
struct faillog fail;
off_t offset_uid = (off_t) (sizeof *fl) * uid;
/*
* Suppress the check if the log file isn't there.
*/
if (access (FAILLOG_FILE, F_OK) != 0) {
return 1;
}
fd = open (FAILLOG_FILE, failed?O_RDONLY:O_RDWR);
if (fd < 0) {
SYSLOG ((LOG_WARN,
"Can't open the faillog file (%s) to check UID %lu: %m; "
"User access authorized.",
FAILLOG_FILE, (unsigned long) uid));
return 1;
}
/*
* Get the record from the file and determine if the user has
* exceeded the failure limit. If "max" is zero, any number
* of failures are permitted. Only when "max" is non-zero and
* "cnt" is greater than or equal to "max" is the account
* considered to be locked.
*
* If read fails, there is no record for this user yet (the
* file is initially zero length and extended by writes), so
* no need to reset the count.
*/
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (read (fd, fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
(void) close (fd);
return 1;
}
if (too_many_failures (fl)) {
(void) close (fd);
return 0;
}
/*
* The record is updated if this is not a failure. The count will
* be reset to zero, but the rest of the information will be left
* in the record in case someone wants to see where the failed
* login originated.
*/
if (!failed) {
fail = *fl;
fail.fail_cnt = 0;
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (write_full(fd, &fail, sizeof fail) == -1)) {
goto err_write;
}
if (close (fd) != 0 && errno != EINTR) {
goto err_close;
}
} else {
(void) close (fd);
}
return 1;
err_write:
{
int saved_errno = errno;
(void) close (fd);
errno = saved_errno;
}
err_close:
SYSLOG ((LOG_WARN,
"Can't reset faillog entry for UID %lu in %s: %m",
(unsigned long) uid, FAILLOG_FILE));
return 1;
}
/*
* failprint - print line of failure information
*
* failprint takes a (struct faillog) entry and formats it into a
* message which is displayed at login time.
*/
void failprint (const struct faillog *fail)
{
struct tm *tp;
char lasttimeb[256];
char *lasttime = lasttimeb;
time_t NOW;
if (0 == fail->fail_cnt) {
return;
}
tp = localtime (&(fail->fail_time));
(void) time (&NOW);
/*
* Print all information we have.
*/
STRFTIME(lasttimeb, "%c", tp);
/*@-formatconst@*/
(void) printf (ngettext ("%d failure since last login.\n"
"Last was %s on %s.\n",
"%d failures since last login.\n"
"Last was %s on %s.\n",
(unsigned long) fail->fail_cnt),
fail->fail_cnt, lasttime, fail->fail_line);
/*@=formatconst@*/
}
+44
View File
@@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1997 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2005 , Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* $Id$ */
#ifndef _FAILURE_H_
#define _FAILURE_H_
#include "defines.h"
#include "faillog.h"
/*
* failure - make failure entry
*
* failure() creates a new (struct faillog) entry or updates an
* existing one with the current failed login information.
*/
extern void failure (uid_t, const char *, struct faillog *);
/*
* failcheck - check for failures > allowable
*
* failcheck() is called AFTER the password has been validated. If the
* account has been "attacked" with too many login failures, failcheck()
* returns FALSE to indicate that the login should be denied even though
* the password is valid.
*/
extern int failcheck (uid_t uid, struct faillog *fl, bool failed);
/*
* failprint - print line of failure information
*
* failprint takes a (struct faillog) entry and formats it into a
* message which is displayed at login time.
*/
extern void failprint (const struct faillog *);
#endif
+41
View File
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2024, Skyler Ferrante <sjf5462@rit.edu>
// SPDX-License-Identifier: BSD-3-Clause
/**
* To protect against file descriptor omission attacks, we open the std file
* descriptors with /dev/null if they are not already open. Code is based on
* fix_fds from sudo.c.
*/
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "prototypes.h"
static void check_fd(int fd);
void
check_fds(void)
{
/**
* Make sure stdin, stdout, stderr are open
* If they are closed, set them to /dev/null
*/
check_fd(STDIN_FILENO);
check_fd(STDOUT_FILENO);
check_fd(STDERR_FILENO);
}
static void
check_fd(int fd)
{
int devnull;
if (fcntl(fd, F_GETFL, 0) != -1)
return;
devnull = open("/dev/null", O_RDWR);
if (devnull != fd)
abort();
}
+20 -22
View File
@@ -21,9 +21,9 @@
*
* The supplied field is scanned for non-printable and other illegal
* characters.
* + -1 is returned if an illegal character is present.
* + 1 is returned if no illegal characters are present, but the field
* contains a non-printable character.
* + -1 is returned if an illegal or control character is present.
* + 1 is returned if no illegal or control characters are present,
* but the field contains a non-printable character.
* + 0 is returned otherwise.
*/
int valid_field (const char *field, const char *illegal)
@@ -37,20 +37,19 @@ int valid_field (const char *field, const char *illegal)
/* For each character of field, search if it appears in the list
* of illegal characters. */
for (cp = field; '\0' != *cp; cp++) {
if (strchr (illegal, *cp) != NULL) {
err = -1;
break;
}
if (illegal && NULL != strpbrk (field, illegal)) {
return -1;
}
if (0 == err) {
/* Search if there are some non-printable characters */
for (cp = field; '\0' != *cp; cp++) {
if (!isprint (*cp)) {
err = 1;
break;
}
/* Search if there are non-printable or control characters */
for (cp = field; '\0' != *cp; cp++) {
unsigned char c = *cp;
if (!isprint (c)) {
err = 1;
}
if (iscntrl (c)) {
err = -1;
break;
}
}
@@ -74,7 +73,7 @@ void change_field (char *buf, size_t maxsize, const char *prompt)
printf ("\t%s [%s]: ", prompt, buf);
(void) fflush (stdout);
if (fgets (newf, (int) maxsize, stdin) != newf) {
if (fgets (newf, maxsize, stdin) != newf) {
return;
}
@@ -91,17 +90,16 @@ void change_field (char *buf, size_t maxsize, const char *prompt)
* entering a space. --marekm
*/
while (--cp >= newf && isspace (*cp));
cp++;
while (newf < cp && isspace (cp[-1])) {
cp--;
}
*cp = '\0';
cp = newf;
while (('\0' != *cp) && isspace (*cp)) {
while (isspace (*cp)) {
cp++;
}
strncpy (buf, cp, maxsize - 1);
buf[maxsize - 1] = '\0';
strcpy (buf, cp);
}
}
+500
View File
@@ -0,0 +1,500 @@
/*
* SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 2008 - 2011, Nicolas François
* SPDX-FileCopyrightText: 2014, Red Hat, Inc.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include "alloc.h"
#include "prototypes.h"
#include "groupio.h"
#include "getdef.h"
#include "shadowlog.h"
/*
* get_ranges - Get the minimum and maximum ID ranges for the search
*
* This function will return the minimum and maximum ranges for IDs
*
* 0: The function completed successfully
* EINVAL: The provided ranges are impossible (such as maximum < minimum)
*
* preferred_min: The special-case minimum value for a specifically-
* requested ID, which may be lower than the standard min_id
*/
static int get_ranges (bool sys_group, gid_t *min_id, gid_t *max_id,
gid_t *preferred_min)
{
gid_t gid_def_max = 0;
if (sys_group) {
/* System groups */
/* A requested ID is allowed to be below the autoselect range */
*preferred_min = (gid_t) 1;
/* Get the minimum ID range from login.defs or default to 101 */
*min_id = getdef_ulong ("SYS_GID_MIN", 101UL);
/*
* If SYS_GID_MAX is unspecified, we should assume it to be one
* less than the GID_MIN (which is reserved for non-system accounts)
*/
gid_def_max = getdef_ulong ("GID_MIN", 1000UL) - 1;
*max_id = getdef_ulong ("SYS_GID_MAX", gid_def_max);
/* Check that the ranges make sense */
if (*max_id < *min_id) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: SYS_GID_MIN (%lu), "
"GID_MIN (%lu), SYS_GID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
getdef_ulong ("GID_MIN", 1000UL),
(unsigned long) *max_id);
return EINVAL;
}
/*
* Zero is reserved for root and the allocation algorithm does not
* work right with it.
*/
if (*min_id == 0) {
*min_id = (gid_t) 1;
}
} else {
/* Non-system groups */
/* Get the values from login.defs or use reasonable defaults */
*min_id = getdef_ulong ("GID_MIN", 1000UL);
*max_id = getdef_ulong ("GID_MAX", 60000UL);
/*
* The preferred minimum should match the standard ID minimum
* for non-system groups.
*/
*preferred_min = *min_id;
/* Check that the ranges make sense */
if (*max_id < *min_id) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: GID_MIN (%lu), "
"GID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
(unsigned long) *max_id);
return EINVAL;
}
}
return 0;
}
/*
* check_gid - See if the requested GID is available
*
* On success, return 0
* If the ID is in use, return EEXIST
* If the ID might clash with -1, return EINVAL
* If the ID is outside the range, return ERANGE
* In other cases, return errno from getgrgid()
*/
static int check_gid (const gid_t gid,
const gid_t gid_min,
const gid_t gid_max,
const bool *used_gids)
{
/* First test that the preferred ID is in the range */
if (gid < gid_min || gid > gid_max) {
return ERANGE;
}
/* Check for compatibility with 16b and 32b gid_t error codes */
if (gid == UINT16_MAX || gid == UINT32_MAX) {
return EINVAL;
}
/*
* Check whether we already detected this GID
* using the gr_next() loop
*/
if (used_gids != NULL && used_gids[gid]) {
return EEXIST;
}
/* Check if the GID exists according to NSS */
errno = 0;
if (prefix_getgrgid (gid) != NULL) {
return EEXIST;
} else {
/* getgrgid() was NULL
* we have to ignore errors as temporary
* failures of remote user identity services
* would completely block user/group creation
*/
}
/* If we've made it here, the GID must be available */
return 0;
}
/*
* find_new_gid - Find a new unused GID.
*
* If successful, find_new_gid provides an unused group ID in the
* [GID_MIN:GID_MAX] range.
* This ID should be higher than all the used GID, but if not possible,
* the lowest unused ID in the range will be returned.
*
* Return 0 on success, -1 if no unused GIDs are available.
*/
int find_new_gid (bool sys_group,
gid_t *gid,
/*@null@*/gid_t const *preferred_gid)
{
bool *used_gids;
const struct group *grp;
gid_t gid_min, gid_max, preferred_min;
gid_t id;
gid_t lowest_found, highest_found;
int result;
int nospam = 0;
assert(gid != NULL);
/*
* First, figure out what ID range is appropriate for
* automatic assignment
*/
result = get_ranges (sys_group, &gid_min, &gid_max, &preferred_min);
if (result == EINVAL) {
return -1;
}
/* Check if the preferred GID is available */
if (preferred_gid) {
result = check_gid (*preferred_gid, preferred_min, gid_max, NULL);
if (result == 0) {
/*
* Make sure the GID isn't queued for use already
*/
if (gr_locate_gid (*preferred_gid) == NULL) {
*gid = *preferred_gid;
return 0;
}
/*
* gr_locate_gid() found the GID in an as-yet uncommitted
* entry. We'll proceed below and auto-set a GID.
*/
} else if (result == EEXIST || result == ERANGE || result == EINVAL) {
/*
* Continue on below. At this time, we won't
* treat these three cases differently.
*/
} else {
/*
* An unexpected error occurred. We should report
* this and fail the group creation.
* This differs from the automatic creation
* behavior below, since if a specific GID was
* requested and generated an error, the user is
* more likely to want to stop and address the
* issue.
*/
fprintf (log_get_logfd(),
_("%s: Encountered error attempting to use "
"preferred GID: %s\n"),
log_get_progname(), strerror (result));
return -1;
}
}
/*
* Search the entire group file,
* looking for the next unused value.
*
* We first check the local database with gr_rewind/gr_next to find
* all local values that are in use.
*
* We then compare the next free value to all databases (local and
* remote) and iterate until we find a free one. If there are free
* values beyond the lowest (system groups) or highest (non-system
* groups), we will prefer those and avoid potentially reclaiming a
* deleted group (which can be a security issue, since it may grant
* access to files belonging to that former group).
*
* If there are no GIDs available at the end of the search, we will
* have no choice but to iterate through the range looking for gaps.
*
*/
/* Create an array to hold all of the discovered GIDs */
used_gids = CALLOC (gid_max + 1, bool);
if (NULL == used_gids) {
fprintf (log_get_logfd(),
_("%s: failed to allocate memory: %s\n"),
log_get_progname(), strerror (errno));
return -1;
}
/* First look for the lowest and highest value in the local database */
(void) gr_rewind ();
highest_found = gid_min;
lowest_found = gid_max;
while ((grp = gr_next ()) != NULL) {
/*
* Does this entry have a lower GID than the lowest we've found
* so far?
*/
if ((grp->gr_gid <= lowest_found) && (grp->gr_gid >= gid_min)) {
lowest_found = grp->gr_gid - 1;
}
/*
* Does this entry have a higher GID than the highest we've found
* so far?
*/
if ((grp->gr_gid >= highest_found) && (grp->gr_gid <= gid_max)) {
highest_found = grp->gr_gid + 1;
}
/* create index of used GIDs */
if (grp->gr_gid >= gid_min
&& grp->gr_gid <= gid_max) {
used_gids[grp->gr_gid] = true;
}
}
if (sys_group) {
/*
* For system groups, we want to start from the
* top of the range and work downwards.
*/
/*
* At the conclusion of the gr_next() search, we will either
* have a presumed-free GID or we will be at GID_MIN - 1.
*/
if (lowest_found < gid_min) {
/*
* In this case, a GID is in use at GID_MIN.
*
* We will reset the search to GID_MAX and proceed down
* through all the GIDs (skipping those we detected with
* used_gids) for a free one. It is a known issue that
* this may result in reusing a previously-deleted GID,
* so administrators should be instructed to use this
* auto-detection with care (and prefer to assign GIDs
* explicitly).
*/
lowest_found = gid_max;
}
/* Search through all of the IDs in the range */
for (id = lowest_found; id >= gid_min; id--) {
result = check_gid (id, gid_min, gid_max, used_gids);
if (result == 0) {
/* This GID is available. Return it. */
*gid = id;
free (used_gids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique system GID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG ((LOG_ERR,
"Error checking available GIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later GID
* will work properly.
*/
}
}
/*
* If we get all the way through the loop, try again from GID_MAX,
* unless that was where we previously started. (NOTE: the worst-case
* scenario here is that we will run through (GID_MAX - GID_MIN - 1)
* cycles *again* if we fall into this case with lowest_found as
* GID_MAX - 1, all groups in the range in use and maintained by
* network services such as LDAP.)
*/
if (lowest_found != gid_max) {
for (id = gid_max; id >= gid_min; id--) {
result = check_gid (id, gid_min, gid_max, used_gids);
if (result == 0) {
/* This GID is available. Return it. */
*gid = id;
free (used_gids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique system GID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG ((LOG_ERR,
"Error checking available GIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later GID
* will work properly.
*/
}
}
}
} else { /* !sys_group */
/*
* For non-system groups, we want to start from the
* bottom of the range and work upwards.
*/
/*
* At the conclusion of the gr_next() search, we will either
* have a presumed-free GID or we will be at GID_MAX + 1.
*/
if (highest_found > gid_max) {
/*
* In this case, a GID is in use at GID_MAX.
*
* We will reset the search to GID_MIN and proceed up
* through all the GIDs (skipping those we detected with
* used_gids) for a free one. It is a known issue that
* this may result in reusing a previously-deleted GID,
* so administrators should be instructed to use this
* auto-detection with care (and prefer to assign GIDs
* explicitly).
*/
highest_found = gid_min;
}
/* Search through all of the IDs in the range */
for (id = highest_found; id <= gid_max; id++) {
result = check_gid (id, gid_min, gid_max, used_gids);
if (result == 0) {
/* This GID is available. Return it. */
*gid = id;
free (used_gids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique GID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG ((LOG_ERR,
"Error checking available GIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later GID
* will work properly.
*/
}
}
/*
* If we get all the way through the loop, try again from GID_MIN,
* unless that was where we previously started. (NOTE: the worst-case
* scenario here is that we will run through (GID_MAX - GID_MIN - 1)
* cycles *again* if we fall into this case with highest_found as
* GID_MIN + 1, all groups in the range in use and maintained by
* network services such as LDAP.)
*/
if (highest_found != gid_min) {
for (id = gid_min; id <= gid_max; id++) {
result = check_gid (id, gid_min, gid_max, used_gids);
if (result == 0) {
/* This GID is available. Return it. */
*gid = id;
free (used_gids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique GID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG ((LOG_ERR,
"Error checking available GIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later GID
* will work properly.
*/
}
}
}
}
/* The code reached here and found no available IDs in the range */
fprintf (log_get_logfd(),
_("%s: Can't get unique GID (no more available GIDs)\n"),
log_get_progname());
SYSLOG ((LOG_WARN, "no more available GIDs on the system"));
free (used_gids);
return -1;
}
+64
View File
@@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2012 Eric Biederman
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ifdef ENABLE_SUBIDS
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include "prototypes.h"
#include "subordinateio.h"
#include "getdef.h"
#include "shadowlog.h"
/*
* find_new_sub_gids - Find a new unused range of GIDs.
*
* If successful, find_new_sub_gids provides a range of unused
* user IDs in the [SUB_GID_MIN:SUB_GID_MAX] range.
*
* Return 0 on success, -1 if no unused GIDs are available.
*/
int find_new_sub_gids (gid_t *range_start, unsigned long *range_count)
{
unsigned long min, max;
unsigned long count;
gid_t start;
assert (range_start != NULL);
assert (range_count != NULL);
min = getdef_ulong ("SUB_GID_MIN", 100000UL);
max = getdef_ulong ("SUB_GID_MAX", 600100000UL);
count = getdef_ulong ("SUB_GID_COUNT", 65536);
if (min > max || count >= max || (min + count - 1) > max) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: SUB_GID_MIN (%lu),"
" SUB_GID_MAX (%lu), SUB_GID_COUNT (%lu)\n"),
log_get_progname(), min, max, count);
return -1;
}
start = sub_gid_find_free_range(min, max, count);
if (start == (gid_t)-1) {
fprintf (log_get_logfd(),
_("%s: Can't get unique subordinate GID range\n"),
log_get_progname());
SYSLOG ((LOG_WARN, "no more available subordinate GIDs on the system"));
return -1;
}
*range_start = start;
*range_count = count;
return 0;
}
#else /* !ENABLE_SUBIDS */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* !ENABLE_SUBIDS */
+64
View File
@@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2012 Eric Biederman
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ifdef ENABLE_SUBIDS
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include "prototypes.h"
#include "subordinateio.h"
#include "getdef.h"
#include "shadowlog.h"
/*
* find_new_sub_uids - Find a new unused range of UIDs.
*
* If successful, find_new_sub_uids provides a range of unused
* user IDs in the [SUB_UID_MIN:SUB_UID_MAX] range.
*
* Return 0 on success, -1 if no unused UIDs are available.
*/
int find_new_sub_uids (uid_t *range_start, unsigned long *range_count)
{
unsigned long min, max;
unsigned long count;
uid_t start;
assert (range_start != NULL);
assert (range_count != NULL);
min = getdef_ulong ("SUB_UID_MIN", 100000UL);
max = getdef_ulong ("SUB_UID_MAX", 600100000UL);
count = getdef_ulong ("SUB_UID_COUNT", 65536);
if (min > max || count >= max || (min + count - 1) > max) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: SUB_UID_MIN (%lu),"
" SUB_UID_MAX (%lu), SUB_UID_COUNT (%lu)\n"),
log_get_progname(), min, max, count);
return -1;
}
start = sub_uid_find_free_range(min, max, count);
if (start == (uid_t)-1) {
fprintf (log_get_logfd(),
_("%s: Can't get unique subordinate UID range\n"),
log_get_progname());
SYSLOG ((LOG_WARN, "no more available subordinate UIDs on the system"));
return -1;
}
*range_start = start;
*range_count = count;
return 0;
}
#else /* !ENABLE_SUBIDS */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* !ENABLE_SUBIDS */
+500
View File
@@ -0,0 +1,500 @@
/*
* SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 2008 - 2011, Nicolas François
* SPDX-FileCopyrightText: 2014, Red Hat, Inc.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include "alloc.h"
#include "prototypes.h"
#include "pwio.h"
#include "getdef.h"
#include "shadowlog.h"
/*
* get_ranges - Get the minimum and maximum ID ranges for the search
*
* This function will return the minimum and maximum ranges for IDs
*
* 0: The function completed successfully
* EINVAL: The provided ranges are impossible (such as maximum < minimum)
*
* preferred_min: The special-case minimum value for a specifically-
* requested ID, which may be lower than the standard min_id
*/
static int get_ranges (bool sys_user, uid_t *min_id, uid_t *max_id,
uid_t *preferred_min)
{
uid_t uid_def_max = 0;
if (sys_user) {
/* System users */
/* A requested ID is allowed to be below the autoselect range */
*preferred_min = (uid_t) 1;
/* Get the minimum ID range from login.defs or default to 101 */
*min_id = getdef_ulong ("SYS_UID_MIN", 101UL);
/*
* If SYS_UID_MAX is unspecified, we should assume it to be one
* less than the UID_MIN (which is reserved for non-system accounts)
*/
uid_def_max = getdef_ulong ("UID_MIN", 1000UL) - 1;
*max_id = getdef_ulong ("SYS_UID_MAX", uid_def_max);
/* Check that the ranges make sense */
if (*max_id < *min_id) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: SYS_UID_MIN (%lu), "
"UID_MIN (%lu), SYS_UID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
getdef_ulong ("UID_MIN", 1000UL),
(unsigned long) *max_id);
return EINVAL;
}
/*
* Zero is reserved for root and the allocation algorithm does not
* work right with it.
*/
if (*min_id == 0) {
*min_id = (uid_t) 1;
}
} else {
/* Non-system users */
/* Get the values from login.defs or use reasonable defaults */
*min_id = getdef_ulong ("UID_MIN", 1000UL);
*max_id = getdef_ulong ("UID_MAX", 60000UL);
/*
* The preferred minimum should match the standard ID minimum
* for non-system users.
*/
*preferred_min = *min_id;
/* Check that the ranges make sense */
if (*max_id < *min_id) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: UID_MIN (%lu), "
"UID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
(unsigned long) *max_id);
return EINVAL;
}
}
return 0;
}
/*
* check_uid - See if the requested UID is available
*
* On success, return 0
* If the ID is in use, return EEXIST
* If the ID might clash with -1, return EINVAL
* If the ID is outside the range, return ERANGE
* In other cases, return errno from getpwuid()
*/
static int check_uid(const uid_t uid,
const uid_t uid_min,
const uid_t uid_max,
const bool *used_uids)
{
/* First test that the preferred ID is in the range */
if (uid < uid_min || uid > uid_max) {
return ERANGE;
}
/* Check for compatibility with 16b and 32b uid_t error codes */
if (uid == UINT16_MAX || uid == UINT32_MAX) {
return EINVAL;
}
/*
* Check whether we already detected this UID
* using the pw_next() loop
*/
if (used_uids != NULL && used_uids[uid]) {
return EEXIST;
}
/* Check if the UID exists according to NSS */
errno = 0;
if (prefix_getpwuid(uid) != NULL) {
return EEXIST;
} else {
/* getpwuid() was NULL
* we have to ignore errors as temporary
* failures of remote user identity services
* would completely block user/group creation
*/
}
/* If we've made it here, the UID must be available */
return 0;
}
/*
* find_new_uid - Find a new unused UID.
*
* If successful, find_new_uid provides an unused user ID in the
* [UID_MIN:UID_MAX] range.
* This ID should be higher than all the used UID, but if not possible,
* the lowest unused ID in the range will be returned.
*
* Return 0 on success, -1 if no unused UIDs are available.
*/
int find_new_uid(bool sys_user,
uid_t *uid,
/*@null@*/uid_t const *preferred_uid)
{
bool *used_uids;
const struct passwd *pwd;
uid_t uid_min, uid_max, preferred_min;
uid_t id;
uid_t lowest_found, highest_found;
int result;
int nospam = 0;
assert (uid != NULL);
/*
* First, figure out what ID range is appropriate for
* automatic assignment
*/
result = get_ranges (sys_user, &uid_min, &uid_max, &preferred_min);
if (result == EINVAL) {
return -1;
}
/* Check if the preferred UID is available */
if (preferred_uid) {
result = check_uid (*preferred_uid, preferred_min, uid_max, NULL);
if (result == 0) {
/*
* Make sure the UID isn't queued for use already
*/
if (pw_locate_uid (*preferred_uid) == NULL) {
*uid = *preferred_uid;
return 0;
}
/*
* pw_locate_uid() found the UID in an as-yet uncommitted
* entry. We'll proceed below and auto-set an UID.
*/
} else if (result == EEXIST || result == ERANGE || result == EINVAL) {
/*
* Continue on below. At this time, we won't
* treat these three cases differently.
*/
} else {
/*
* An unexpected error occurred. We should report
* this and fail the user creation.
* This differs from the automatic creation
* behavior below, since if a specific UID was
* requested and generated an error, the user is
* more likely to want to stop and address the
* issue.
*/
fprintf (log_get_logfd(),
_("%s: Encountered error attempting to use "
"preferred UID: %s\n"),
log_get_progname(), strerror (result));
return -1;
}
}
/*
* Search the entire passwd file,
* looking for the next unused value.
*
* We first check the local database with pw_rewind/pw_next to find
* all local values that are in use.
*
* We then compare the next free value to all databases (local and
* remote) and iterate until we find a free one. If there are free
* values beyond the lowest (system users) or highest (non-system
* users), we will prefer those and avoid potentially reclaiming a
* deleted user (which can be a security issue, since it may grant
* access to files belonging to that former user).
*
* If there are no UIDs available at the end of the search, we will
* have no choice but to iterate through the range looking for gaps.
*
*/
/* Create an array to hold all of the discovered UIDs */
used_uids = CALLOC(uid_max + 1, bool);
if (NULL == used_uids) {
fprintf (log_get_logfd(),
_("%s: failed to allocate memory: %s\n"),
log_get_progname(), strerror (errno));
return -1;
}
/* First look for the lowest and highest value in the local database */
(void) pw_rewind ();
highest_found = uid_min;
lowest_found = uid_max;
while ((pwd = pw_next ()) != NULL) {
/*
* Does this entry have a lower UID than the lowest we've found
* so far?
*/
if ((pwd->pw_uid <= lowest_found) && (pwd->pw_uid >= uid_min)) {
lowest_found = pwd->pw_uid - 1;
}
/*
* Does this entry have a higher UID than the highest we've found
* so far?
*/
if ((pwd->pw_uid >= highest_found) && (pwd->pw_uid <= uid_max)) {
highest_found = pwd->pw_uid + 1;
}
/* create index of used UIDs */
if (pwd->pw_uid >= uid_min
&& pwd->pw_uid <= uid_max) {
used_uids[pwd->pw_uid] = true;
}
}
if (sys_user) {
/*
* For system users, we want to start from the
* top of the range and work downwards.
*/
/*
* At the conclusion of the pw_next() search, we will either
* have a presumed-free UID or we will be at UID_MIN - 1.
*/
if (lowest_found < uid_min) {
/*
* In this case, an UID is in use at UID_MIN.
*
* We will reset the search to UID_MAX and proceed down
* through all the UIDs (skipping those we detected with
* used_uids) for a free one. It is a known issue that
* this may result in reusing a previously-deleted UID,
* so administrators should be instructed to use this
* auto-detection with care (and prefer to assign UIDs
* explicitly).
*/
lowest_found = uid_max;
}
/* Search through all of the IDs in the range */
for (id = lowest_found; id >= uid_min; id--) {
result = check_uid (id, uid_min, uid_max, used_uids);
if (result == 0) {
/* This UID is available. Return it. */
*uid = id;
free (used_uids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique system UID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG ((LOG_ERR,
"Error checking available UIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later UID
* will work properly.
*/
}
}
/*
* If we get all the way through the loop, try again from UID_MAX,
* unless that was where we previously started. (NOTE: the worst-case
* scenario here is that we will run through (UID_MAX - UID_MIN - 1)
* cycles *again* if we fall into this case with lowest_found as
* UID_MAX - 1, all users in the range in use and maintained by
* network services such as LDAP.)
*/
if (lowest_found != uid_max) {
for (id = uid_max; id >= uid_min; id--) {
result = check_uid (id, uid_min, uid_max, used_uids);
if (result == 0) {
/* This UID is available. Return it. */
*uid = id;
free (used_uids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique system UID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG((LOG_ERR,
"Error checking available UIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later UID
* will work properly.
*/
}
}
}
} else { /* !sys_user */
/*
* For non-system users, we want to start from the
* bottom of the range and work upwards.
*/
/*
* At the conclusion of the pw_next() search, we will either
* have a presumed-free UID or we will be at UID_MAX + 1.
*/
if (highest_found > uid_max) {
/*
* In this case, a UID is in use at UID_MAX.
*
* We will reset the search to UID_MIN and proceed up
* through all the UIDs (skipping those we detected with
* used_uids) for a free one. It is a known issue that
* this may result in reusing a previously-deleted UID,
* so administrators should be instructed to use this
* auto-detection with care (and prefer to assign UIDs
* explicitly).
*/
highest_found = uid_min;
}
/* Search through all of the IDs in the range */
for (id = highest_found; id <= uid_max; id++) {
result = check_uid (id, uid_min, uid_max, used_uids);
if (result == 0) {
/* This UID is available. Return it. */
*uid = id;
free (used_uids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique UID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG ((LOG_ERR,
"Error checking available UIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later UID
* will work properly.
*/
}
}
/*
* If we get all the way through the loop, try again from UID_MIN,
* unless that was where we previously started. (NOTE: the worst-case
* scenario here is that we will run through (UID_MAX - UID_MIN - 1)
* cycles *again* if we fall into this case with highest_found as
* UID_MIN + 1, all users in the range in use and maintained by
* network services such as LDAP.)
*/
if (highest_found != uid_min) {
for (id = uid_min; id <= uid_max; id++) {
result = check_uid (id, uid_min, uid_max, used_uids);
if (result == 0) {
/* This UID is available. Return it. */
*uid = id;
free (used_uids);
return 0;
} else if (result == EEXIST || result == EINVAL) {
/*
* This GID is in use or unusable, we'll
* continue to the next.
*/
} else {
/*
* An unexpected error occurred.
*
* Only report it the first time to avoid spamming
* the logs
*
*/
if (!nospam) {
fprintf (log_get_logfd(),
_("%s: Can't get unique UID (%s). "
"Suppressing additional messages.\n"),
log_get_progname(), strerror (result));
SYSLOG ((LOG_ERR,
"Error checking available UIDs: %s",
strerror (result)));
nospam = 1;
}
/*
* We will continue anyway. Hopefully a later UID
* will work properly.
*/
}
}
}
}
/* The code reached here and found no available IDs in the range */
fprintf (log_get_logfd(),
_("%s: Can't get unique UID (no more available UIDs)\n"),
log_get_progname());
SYSLOG ((LOG_WARN, "no more available UIDs on the system"));
free (used_uids);
return -1;
}
+2 -1
View File
@@ -16,7 +16,8 @@
#ident "$Id$"
/*@null@*/char *fgetsx (/*@returned@*/ /*@out@*/char *buf, int cnt, FILE * f)
/*@null@*/char *
fgetsx(/*@returned@*/char *restrict buf, int cnt, FILE *restrict f)
{
char *cp = buf;
char *ep;
+30
View File
@@ -0,0 +1,30 @@
/* $OpenBSD: malloc.c,v 1.267 2020/11/23 15:42:11 otto Exp $ */
/*
* Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
void
freezero(void *ptr, size_t sz)
{
/* This is legal. */
if (ptr == NULL)
return;
explicit_bzero(ptr, sz);
free(ptr);
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright © 2005 Aurelien Jarno
* Copyright © 2006 Robert Millan
* Copyright © 2008-2011 Guillem Jover <guillem@hadrons.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LIBBSD_FREEZERO_H
#define LIBBSD_FREEZERO_H
void freezero(void *ptr, size_t size);
#endif
+10 -7
View File
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
@@ -11,21 +12,23 @@
#include "prototypes.h"
#include "defines.h"
int get_gid (const char *gidstr, gid_t *gid)
int
get_gid(const char *gidstr, gid_t *gid)
{
long long int val;
long long val;
char *endptr;
errno = 0;
val = strtoll (gidstr, &endptr, 10);
val = strtoll(gidstr, &endptr, 10);
if ( ('\0' == *gidstr)
|| ('\0' != *endptr)
|| (ERANGE == errno)
|| (0 != errno)
|| (/*@+longintegral@*/val != (gid_t)val)/*@=longintegral@*/) {
return 0;
return -1;
}
*gid = (gid_t)val;
return 1;
*gid = val;
return 0;
}
+77 -6
View File
@@ -10,22 +10,93 @@
#include "prototypes.h"
#include "defines.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "string/sprintf.h"
int get_pid (const char *pidstr, pid_t *pid)
{
long long int val;
long long val;
char *endptr;
errno = 0;
val = strtoll (pidstr, &endptr, 10);
val = strtoll(pidstr, &endptr, 10);
if ( ('\0' == *pidstr)
|| ('\0' != *endptr)
|| (ERANGE == errno)
|| (0 != errno)
|| (val < 1)
|| (/*@+longintegral@*/val != (pid_t)val)/*@=longintegral@*/) {
return 0;
return -1;
}
*pid = (pid_t)val;
return 1;
*pid = val;
return 0;
}
/*
* If use passed in fd:4 as an argument, then return the
* value '4', the fd to use.
* On error, return -1.
*/
int get_pidfd_from_fd(const char *pidfdstr)
{
long long val;
char *endptr;
struct stat st;
dev_t proc_st_dev, proc_st_rdev;
errno = 0;
val = strtoll(pidfdstr, &endptr, 10);
if ( ('\0' == *pidfdstr)
|| ('\0' != *endptr)
|| (0 != errno)
|| (val < 0)
|| (/*@+longintegral@*/val != (int)val)/*@=longintegral@*/) {
return -1;
}
if (stat("/proc/self/uid_map", &st) < 0) {
return -1;
}
proc_st_dev = st.st_dev;
proc_st_rdev = st.st_rdev;
if (fstat(val, &st) < 0) {
return -1;
}
if (st.st_dev != proc_st_dev || st.st_rdev != proc_st_rdev) {
return -1;
}
return (int)val;
}
int open_pidfd(const char *pidstr)
{
int proc_dir_fd;
char proc_dir_name[32];
pid_t target;
if (get_pid(pidstr, &target) == -1)
return -ENOENT;
/* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */
if (SNPRINTF(proc_dir_name, "/proc/%u/", target) == -1) {
fprintf(stderr, "snprintf of proc path failed for %u: %s\n",
target, strerror(errno));
return -EINVAL;
}
proc_dir_fd = open(proc_dir_name, O_DIRECTORY);
if (proc_dir_fd < 0) {
fprintf(stderr, _("Could not open proc directory for target %u: %s\n"),
target, strerror(errno));
return -EINVAL;
}
return proc_dir_fd;
}
+10 -7
View File
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
@@ -11,21 +12,23 @@
#include "prototypes.h"
#include "defines.h"
int get_uid (const char *uidstr, uid_t *uid)
int
get_uid(const char *uidstr, uid_t *uid)
{
long long int val;
long long val;
char *endptr;
errno = 0;
val = strtoll (uidstr, &endptr, 10);
val = strtoll(uidstr, &endptr, 10);
if ( ('\0' == *uidstr)
|| ('\0' != *endptr)
|| (ERANGE == errno)
|| (0 != errno)
|| (/*@+longintegral@*/val != (uid_t)val)/*@=longintegral@*/) {
return 0;
return -1;
}
*uid = (uid_t)val;
return 1;
*uid = val;
return 0;
}
+2535
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1997 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2005 , Tomasz Kłoczko
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _GETDATE_H_
#define _GETDATE_H_
#include <config.h>
#include "defines.h"
time_t get_date (const char *p, /*@null@*/const time_t *now);
#endif
+949
View File
@@ -0,0 +1,949 @@
%{
/*
** Originally written by Steven M. Bellovin <smb@research.att.com> while
** at the University of North Carolina at Chapel Hill. Later tweaked by
** a couple of people on Usenet. Completely overhauled by Rich $alz
** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
**
** This grammar has 13 shift/reduce conflicts.
**
** This code is in the public domain and has no copyright.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/* Since the code of getdate.y is not included in the Emacs executable
itself, there is no need to #define static in this file. Even if
the code were included in the Emacs executable, it probably
wouldn't do any harm to #undef it here; this will only cause
problems if we try to write to a static variable, which I don't
think this code needs to do. */
#ifdef emacs
# undef static
#endif
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include "attr.h"
#include "getdate.h"
#include <string.h>
/* Some old versions of bison generate parsers that use bcopy.
That loses on systems that don't provide the function, so we have
to redefine it here. */
#if !defined (HAVE_BCOPY) && !defined (bcopy)
# define bcopy(from, to, len) memcpy ((to), (from), (len))
#endif
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
as well as gratuitously global symbol names, so we can have multiple
yacc generated parsers in the same program. Note that these are only
the variables produced by yacc. If other parser generators (bison,
byacc, etc) produce additional global names that conflict at link time,
then those parser generators need to be fixed instead of adding those
names to this list. */
#define yymaxdepth gd_maxdepth
#define yyparse gd_parse
#define yylex gd_lex
#define yyerror gd_error
#define yylval gd_lval
#define yychar gd_char
#define yydebug gd_debug
#define yypact gd_pact
#define yyr1 gd_r1
#define yyr2 gd_r2
#define yydef gd_def
#define yychk gd_chk
#define yypgo gd_pgo
#define yyact gd_act
#define yyexca gd_exca
#define yyerrflag gd_errflag
#define yynerrs gd_nerrs
#define yyps gd_ps
#define yypv gd_pv
#define yys gd_s
#define yy_yys gd_yys
#define yystate gd_state
#define yytmp gd_tmp
#define yyv gd_v
#define yy_yyv gd_yyv
#define yyval gd_val
#define yylloc gd_lloc
#define yyreds gd_reds /* With YYDEBUG defined */
#define yytoks gd_toks /* With YYDEBUG defined */
#define yylhs gd_yylhs
#define yylen gd_yylen
#define yydefred gd_yydefred
#define yydgoto gd_yydgoto
#define yysindex gd_yysindex
#define yyrindex gd_yyrindex
#define yygindex gd_yygindex
#define yytable gd_yytable
#define yycheck gd_yycheck
static int yylex (void);
static int yyerror (const char *s);
#define EPOCH 1970
#define HOUR(x) ((x) * 60)
#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
/*
** An entry in the lexical lookup table.
*/
typedef struct _TABLE {
const char *name;
int type;
int value;
} TABLE;
/*
** Meridian: am, pm, or 24-hour style.
*/
typedef enum _MERIDIAN {
MERam, MERpm, MER24
} MERIDIAN;
/*
** Global variables. We could get rid of most of these by using a good
** union as the yacc stack. (This routine was originally written before
** yacc had the %union construct.) Maybe someday; right now we only use
** the %union very rarely.
*/
static const char *yyInput;
static int yyDayOrdinal;
static int yyDayNumber;
static int yyHaveDate;
static int yyHaveDay;
static int yyHaveRel;
static int yyHaveTime;
static int yyHaveZone;
static int yyTimezone;
static int yyDay;
static int yyHour;
static int yyMinutes;
static int yyMonth;
static int yySeconds;
static int yyYear;
static MERIDIAN yyMeridian;
static int yyRelDay;
static int yyRelHour;
static int yyRelMinutes;
static int yyRelMonth;
static int yyRelSeconds;
static int yyRelYear;
%}
%union {
int Number;
enum _MERIDIAN Meridian;
}
%token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
%token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
%token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
%type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
%type <Number> tMONTH tMONTH_UNIT
%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
%type <Meridian> tMERIDIAN o_merid
%%
spec : /* NULL */
| spec item
;
item : time {
yyHaveTime++;
}
| zone {
yyHaveZone++;
}
| date {
yyHaveDate++;
}
| day {
yyHaveDay++;
}
| rel {
yyHaveRel++;
}
| number
;
time : tUNUMBER tMERIDIAN {
yyHour = $1;
yyMinutes = 0;
yySeconds = 0;
yyMeridian = $2;
}
| tUNUMBER ':' tUNUMBER o_merid {
yyHour = $1;
yyMinutes = $3;
yySeconds = 0;
yyMeridian = $4;
}
| tUNUMBER ':' tUNUMBER tSNUMBER {
yyHour = $1;
yyMinutes = $3;
yyMeridian = MER24;
yyHaveZone++;
yyTimezone = ($4 < 0
? -$4 % 100 + (-$4 / 100) * 60
: - ($4 % 100 + ($4 / 100) * 60));
}
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
yyHour = $1;
yyMinutes = $3;
yySeconds = $5;
yyMeridian = $6;
}
| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
yyHour = $1;
yyMinutes = $3;
yySeconds = $5;
yyMeridian = MER24;
yyHaveZone++;
yyTimezone = ($6 < 0
? -$6 % 100 + (-$6 / 100) * 60
: - ($6 % 100 + ($6 / 100) * 60));
}
;
zone : tZONE {
yyTimezone = $1;
}
| tDAYZONE {
yyTimezone = $1 - 60;
}
|
tZONE tDST {
yyTimezone = $1 - 60;
}
;
day : tDAY {
yyDayOrdinal = 1;
yyDayNumber = $1;
}
| tDAY ',' {
yyDayOrdinal = 1;
yyDayNumber = $1;
}
| tUNUMBER tDAY {
yyDayOrdinal = $1;
yyDayNumber = $2;
}
;
date : tUNUMBER '/' tUNUMBER {
yyMonth = $1;
yyDay = $3;
}
| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
/* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
The goal in recognizing YYYY/MM/DD is solely to support legacy
machine-generated dates like those in an RCS log listing. If
you want portability, use the ISO 8601 format. */
if ($1 >= 1000)
{
yyYear = $1;
yyMonth = $3;
yyDay = $5;
}
else
{
yyMonth = $1;
yyDay = $3;
yyYear = $5;
}
}
| tUNUMBER tSNUMBER tSNUMBER {
/* ISO 8601 format. yyyy-mm-dd. */
yyYear = $1;
yyMonth = -$2;
yyDay = -$3;
}
| tUNUMBER tMONTH tSNUMBER {
/* e.g. 17-JUN-1992. */
yyDay = $1;
yyMonth = $2;
yyYear = -$3;
}
| tMONTH tUNUMBER {
yyMonth = $1;
yyDay = $2;
}
| tMONTH tUNUMBER ',' tUNUMBER {
yyMonth = $1;
yyDay = $2;
yyYear = $4;
}
| tUNUMBER tMONTH {
yyMonth = $2;
yyDay = $1;
}
| tUNUMBER tMONTH tUNUMBER {
yyMonth = $2;
yyDay = $1;
yyYear = $3;
}
;
rel : relunit tAGO {
yyRelSeconds = -yyRelSeconds;
yyRelMinutes = -yyRelMinutes;
yyRelHour = -yyRelHour;
yyRelDay = -yyRelDay;
yyRelMonth = -yyRelMonth;
yyRelYear = -yyRelYear;
}
| relunit
;
relunit : tUNUMBER tYEAR_UNIT {
yyRelYear += $1 * $2;
}
| tSNUMBER tYEAR_UNIT {
yyRelYear += $1 * $2;
}
| tYEAR_UNIT {
yyRelYear += $1;
}
| tUNUMBER tMONTH_UNIT {
yyRelMonth += $1 * $2;
}
| tSNUMBER tMONTH_UNIT {
yyRelMonth += $1 * $2;
}
| tMONTH_UNIT {
yyRelMonth += $1;
}
| tUNUMBER tDAY_UNIT {
yyRelDay += $1 * $2;
}
| tSNUMBER tDAY_UNIT {
yyRelDay += $1 * $2;
}
| tDAY_UNIT {
yyRelDay += $1;
}
| tUNUMBER tHOUR_UNIT {
yyRelHour += $1 * $2;
}
| tSNUMBER tHOUR_UNIT {
yyRelHour += $1 * $2;
}
| tHOUR_UNIT {
yyRelHour += $1;
}
| tUNUMBER tMINUTE_UNIT {
yyRelMinutes += $1 * $2;
}
| tSNUMBER tMINUTE_UNIT {
yyRelMinutes += $1 * $2;
}
| tMINUTE_UNIT {
yyRelMinutes += $1;
}
| tUNUMBER tSEC_UNIT {
yyRelSeconds += $1 * $2;
}
| tSNUMBER tSEC_UNIT {
yyRelSeconds += $1 * $2;
}
| tSEC_UNIT {
yyRelSeconds += $1;
}
;
number : tUNUMBER
{
if ((yyHaveTime != 0) && (yyHaveDate != 0) && (yyHaveRel == 0))
yyYear = $1;
else
{
if ($1>10000)
{
yyHaveDate++;
yyDay= ($1)%100;
yyMonth= ($1/100)%100;
yyYear = $1/10000;
}
else
{
yyHaveTime++;
if ($1 < 100)
{
yyHour = $1;
yyMinutes = 0;
}
else
{
yyHour = $1 / 100;
yyMinutes = $1 % 100;
}
yySeconds = 0;
yyMeridian = MER24;
}
}
}
;
o_merid : /* NULL */
{
$$ = MER24;
}
| tMERIDIAN
{
$$ = $1;
}
;
%%
/* Month and day table. */
static TABLE const MonthDayTable[] = {
{ "january", tMONTH, 1 },
{ "february", tMONTH, 2 },
{ "march", tMONTH, 3 },
{ "april", tMONTH, 4 },
{ "may", tMONTH, 5 },
{ "june", tMONTH, 6 },
{ "july", tMONTH, 7 },
{ "august", tMONTH, 8 },
{ "september", tMONTH, 9 },
{ "sept", tMONTH, 9 },
{ "october", tMONTH, 10 },
{ "november", tMONTH, 11 },
{ "december", tMONTH, 12 },
{ "sunday", tDAY, 0 },
{ "monday", tDAY, 1 },
{ "tuesday", tDAY, 2 },
{ "tues", tDAY, 2 },
{ "wednesday", tDAY, 3 },
{ "wednes", tDAY, 3 },
{ "thursday", tDAY, 4 },
{ "thur", tDAY, 4 },
{ "thurs", tDAY, 4 },
{ "friday", tDAY, 5 },
{ "saturday", tDAY, 6 },
{ NULL, 0, 0 }
};
/* Time units table. */
static TABLE const UnitsTable[] = {
{ "year", tYEAR_UNIT, 1 },
{ "month", tMONTH_UNIT, 1 },
{ "fortnight", tDAY_UNIT, 14 },
{ "week", tDAY_UNIT, 7 },
{ "day", tDAY_UNIT, 1 },
{ "hour", tHOUR_UNIT, 1 },
{ "minute", tMINUTE_UNIT, 1 },
{ "min", tMINUTE_UNIT, 1 },
{ "second", tSEC_UNIT, 1 },
{ "sec", tSEC_UNIT, 1 },
{ NULL, 0, 0 }
};
/* Assorted relative-time words. */
static TABLE const OtherTable[] = {
{ "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
{ "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
{ "today", tMINUTE_UNIT, 0 },
{ "now", tMINUTE_UNIT, 0 },
{ "last", tUNUMBER, -1 },
{ "this", tMINUTE_UNIT, 0 },
{ "next", tUNUMBER, 2 },
{ "first", tUNUMBER, 1 },
/* { "second", tUNUMBER, 2 }, */
{ "third", tUNUMBER, 3 },
{ "fourth", tUNUMBER, 4 },
{ "fifth", tUNUMBER, 5 },
{ "sixth", tUNUMBER, 6 },
{ "seventh", tUNUMBER, 7 },
{ "eighth", tUNUMBER, 8 },
{ "ninth", tUNUMBER, 9 },
{ "tenth", tUNUMBER, 10 },
{ "eleventh", tUNUMBER, 11 },
{ "twelfth", tUNUMBER, 12 },
{ "ago", tAGO, 1 },
{ NULL, 0, 0 }
};
/* The timezone table. */
static TABLE const TimezoneTable[] = {
{ "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
{ "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
{ "utc", tZONE, HOUR ( 0) },
{ "wet", tZONE, HOUR ( 0) }, /* Western European */
{ "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
{ "wat", tZONE, HOUR ( 1) }, /* West Africa */
{ "at", tZONE, HOUR ( 2) }, /* Azores */
{ "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
{ "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
{ "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
{ "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
{ "cst", tZONE, HOUR ( 6) }, /* Central Standard */
{ "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
{ "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
{ "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
{ "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
{ "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
{ "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
{ "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
{ "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
{ "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
{ "cat", tZONE, HOUR (10) }, /* Central Alaska */
{ "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
{ "nt", tZONE, HOUR (11) }, /* Nome */
{ "idlw", tZONE, HOUR (12) }, /* International Date Line West */
{ "cet", tZONE, -HOUR (1) }, /* Central European */
{ "met", tZONE, -HOUR (1) }, /* Middle European */
{ "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
{ "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
{ "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
{ "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
{ "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
{ "fwt", tZONE, -HOUR (1) }, /* French Winter */
{ "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
{ "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
{ "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
{ "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
{ "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
{ "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
{ "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
{ "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
{ "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
{ "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
{ "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
{ "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
{ "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
{ "nzt", tZONE, -HOUR (12) }, /* New Zealand */
{ "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
{ "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
{ "idle", tZONE, -HOUR (12) }, /* International Date Line East */
{ NULL, 0, 0 }
};
/* Military timezone table. */
static TABLE const MilitaryTable[] = {
{ "a", tZONE, HOUR ( 1) },
{ "b", tZONE, HOUR ( 2) },
{ "c", tZONE, HOUR ( 3) },
{ "d", tZONE, HOUR ( 4) },
{ "e", tZONE, HOUR ( 5) },
{ "f", tZONE, HOUR ( 6) },
{ "g", tZONE, HOUR ( 7) },
{ "h", tZONE, HOUR ( 8) },
{ "i", tZONE, HOUR ( 9) },
{ "k", tZONE, HOUR ( 10) },
{ "l", tZONE, HOUR ( 11) },
{ "m", tZONE, HOUR ( 12) },
{ "n", tZONE, HOUR (- 1) },
{ "o", tZONE, HOUR (- 2) },
{ "p", tZONE, HOUR (- 3) },
{ "q", tZONE, HOUR (- 4) },
{ "r", tZONE, HOUR (- 5) },
{ "s", tZONE, HOUR (- 6) },
{ "t", tZONE, HOUR (- 7) },
{ "u", tZONE, HOUR (- 8) },
{ "v", tZONE, HOUR (- 9) },
{ "w", tZONE, HOUR (-10) },
{ "x", tZONE, HOUR (-11) },
{ "y", tZONE, HOUR (-12) },
{ "z", tZONE, HOUR ( 0) },
{ NULL, 0, 0 }
};
static int yyerror (MAYBE_UNUSED const char *s)
{
return 0;
}
static int ToHour (int Hours, MERIDIAN Meridian)
{
switch (Meridian)
{
case MER24:
if (Hours < 0 || Hours > 23)
return -1;
return Hours;
case MERam:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return Hours;
case MERpm:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return Hours + 12;
default:
abort ();
}
/* NOTREACHED */
}
static int ToYear (int Year)
{
if (Year < 0)
Year = -Year;
/* XPG4 suggests that years 00-68 map to 2000-2068, and
years 69-99 map to 1969-1999. */
if (Year < 69)
Year += 2000;
else if (Year < 100)
Year += 1900;
return Year;
}
static int LookupWord (char *buff)
{
register char *p;
register char *q;
register const TABLE *tp;
int i;
bool abbrev;
/* Make it lowercase. */
for (p = buff; '\0' != *p; p++)
if (isupper (*p))
*p = tolower (*p);
if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
{
yylval.Meridian = MERam;
return tMERIDIAN;
}
if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
{
yylval.Meridian = MERpm;
return tMERIDIAN;
}
/* See if we have an abbreviation for a month. */
if (strlen (buff) == 3)
abbrev = true;
else if (strlen (buff) == 4 && buff[3] == '.')
{
abbrev = true;
buff[3] = '\0';
}
else
abbrev = false;
for (tp = MonthDayTable; tp->name; tp++)
{
if (abbrev)
{
if (strncmp (buff, tp->name, 3) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
}
else if (strcmp (buff, tp->name) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
}
for (tp = TimezoneTable; tp->name; tp++)
if (strcmp (buff, tp->name) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
if (strcmp (buff, "dst") == 0)
return tDST;
for (tp = UnitsTable; tp->name; tp++)
if (strcmp (buff, tp->name) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
/* Strip off any plural and try the units table again. */
i = strlen (buff) - 1;
if (buff[i] == 's')
{
buff[i] = '\0';
for (tp = UnitsTable; tp->name; tp++)
if (strcmp (buff, tp->name) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
buff[i] = 's'; /* Put back for "this" in OtherTable. */
}
for (tp = OtherTable; tp->name; tp++)
if (strcmp (buff, tp->name) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
/* Military timezones. */
if (buff[1] == '\0' && isalpha (*buff))
{
for (tp = MilitaryTable; tp->name; tp++)
if (strcmp (buff, tp->name) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
}
/* Drop out any periods and try the timezone table again. */
for (i = 0, p = q = buff; '\0' != *q; q++)
if (*q != '.')
*p++ = *q;
else
i++;
*p = '\0';
if (0 != i)
for (tp = TimezoneTable; NULL != tp->name; tp++)
if (strcmp (buff, tp->name) == 0)
{
yylval.Number = tp->value;
return tp->type;
}
return tID;
}
static int
yylex (void)
{
register char c;
register char *p;
char buff[20];
int Count;
int sign;
for (;;)
{
while (isspace (*yyInput))
yyInput++;
if (isdigit (c = *yyInput) || c == '-' || c == '+')
{
if (c == '-' || c == '+')
{
sign = c == '-' ? -1 : 1;
if (!isdigit (*++yyInput))
/* skip the '-' sign */
continue;
}
else
sign = 0;
for (yylval.Number = 0; isdigit (c = *yyInput++);)
yylval.Number = 10 * yylval.Number + c - '0';
yyInput--;
if (sign < 0)
yylval.Number = -yylval.Number;
return (0 != sign) ? tSNUMBER : tUNUMBER;
}
if (isalpha (c))
{
for (p = buff; (c = *yyInput++, isalpha (c)) || c == '.';)
if (p < &buff[sizeof buff - 1])
*p++ = c;
*p = '\0';
yyInput--;
return LookupWord (buff);
}
if (c != '(')
return *yyInput++;
Count = 0;
do
{
c = *yyInput++;
if (c == '\0')
return c;
if (c == '(')
Count++;
else if (c == ')')
Count--;
}
while (Count > 0);
}
}
#define TM_YEAR_ORIGIN 1900
/* Yield A - B, measured in seconds. */
static long difftm (struct tm *a, struct tm *b)
{
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
long days = (
/* difference in day of year */
a->tm_yday - b->tm_yday
/* + intervening leap days */
+ ((ay >> 2) - (by >> 2))
- (ay / 100 - by / 100)
+ ((ay / 100 >> 2) - (by / 100 >> 2))
/* + difference in years * 365 */
+ (long) (ay - by) * 365
);
return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+ (a->tm_min - b->tm_min))
+ (a->tm_sec - b->tm_sec));
}
time_t get_date (const char *p, const time_t *now)
{
struct tm tm, tm0, *tmp;
time_t Start;
yyInput = p;
Start = now ? *now : time ((time_t *) NULL);
tmp = localtime (&Start);
yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
yyMonth = tmp->tm_mon + 1;
yyDay = tmp->tm_mday;
yyHour = tmp->tm_hour;
yyMinutes = tmp->tm_min;
yySeconds = tmp->tm_sec;
yyMeridian = MER24;
yyRelSeconds = 0;
yyRelMinutes = 0;
yyRelHour = 0;
yyRelDay = 0;
yyRelMonth = 0;
yyRelYear = 0;
yyHaveDate = 0;
yyHaveDay = 0;
yyHaveRel = 0;
yyHaveTime = 0;
yyHaveZone = 0;
if (yyparse ()
|| yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
return -1;
tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
tm.tm_mon = yyMonth - 1 + yyRelMonth;
tm.tm_mday = yyDay + yyRelDay;
if ((yyHaveTime != 0) ||
( (yyHaveRel != 0) && (yyHaveDate == 0) && (yyHaveDay == 0) ))
{
tm.tm_hour = ToHour (yyHour, yyMeridian);
if (tm.tm_hour < 0)
return -1;
tm.tm_min = yyMinutes;
tm.tm_sec = yySeconds;
}
else
{
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
}
tm.tm_hour += yyRelHour;
tm.tm_min += yyRelMinutes;
tm.tm_sec += yyRelSeconds;
tm.tm_isdst = -1;
tm0 = tm;
Start = mktime (&tm);
if (Start == (time_t) -1)
{
/* Guard against falsely reporting errors near the time_t boundaries
when parsing times in other time zones. For example, if the min
time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
of UTC, then the min localtime value is 1970-01-01 08:00:00; if
we apply mktime to 1970-01-01 00:00:00 we will get an error, so
we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
zone by 24 hours to compensate. This algorithm assumes that
there is no DST transition within a day of the time_t boundaries. */
if (yyHaveZone)
{
tm = tm0;
if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
{
tm.tm_mday++;
yyTimezone -= 24 * 60;
}
else
{
tm.tm_mday--;
yyTimezone += 24 * 60;
}
Start = mktime (&tm);
}
if (Start == (time_t) -1)
return Start;
}
if (yyHaveDay && !yyHaveDate)
{
tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
+ 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
Start = mktime (&tm);
if (Start == (time_t) -1)
return Start;
}
if (yyHaveZone)
{
long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
if ((Start + delta < Start) != (delta < 0))
return -1; /* time_t overflow */
Start += delta;
}
return Start;
}
#if defined (TEST)
int
main(void)
{
char buff[MAX_BUFF_LEN + 1];
time_t d;
(void) printf ("Enter date, or blank line to exit.\n\t> ");
(void) fflush (stdout);
buff[MAX_BUFF_LEN] = 0;
while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
{
d = get_date (buff, (time_t *) NULL);
if (d == -1)
(void) printf ("Bad format - couldn't convert.\n");
else
(void) printf ("%s", ctime (&d));
(void) printf ("\t> ");
(void) fflush (stdout);
}
exit (0);
/* NOTREACHED */
}
#endif /* defined (TEST) */
+63 -53
View File
@@ -13,6 +13,7 @@
#include "prototypes.h"
#include "defines.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
@@ -20,8 +21,13 @@
#ifdef USE_ECONF
#include <libeconf.h>
#endif
#include "alloc.h"
#include "getdef.h"
#include "shadowlog_internal.h"
#include "string/sprintf.h"
/*
* A configuration item definition.
*/
@@ -33,7 +39,6 @@ struct itemdef {
#define PAMDEFS \
{"CHFN_AUTH", NULL}, \
{"CHSH_AUTH", NULL}, \
{"CRACKLIB_DICTPATH", NULL}, \
{"ENV_HZ", NULL}, \
{"ENVIRON_FILE", NULL}, \
{"ENV_TZ", NULL}, \
@@ -132,10 +137,8 @@ static struct itemdef def_table[] = {
#ifndef USE_PAM
PAMDEFS
#endif
#ifdef USE_SYSLOG
{"SYSLOG_SG_ENAB", NULL},
{"SYSLOG_SU_ENAB", NULL},
#endif
#ifdef WITH_TCB
{"TCB_AUTH_GROUP", NULL},
{"TCB_SYMLINKS", NULL},
@@ -173,7 +176,7 @@ static const char* def_fname = LOGINDEFS; /* login config defs file */
static bool def_loaded = false; /* are defs already loaded? */
/* local function prototypes */
static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *);
static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *, const char *);
static void def_load (void);
@@ -192,8 +195,8 @@ static void def_load (void);
def_load ();
}
d = def_find (item);
return ((NULL == d)? (const char *) NULL : d->value);
d = def_find (item, NULL);
return (NULL == d) ? NULL : d->value;
}
@@ -211,7 +214,7 @@ bool getdef_bool (const char *item)
def_load ();
}
d = def_find (item);
d = def_find (item, NULL);
if ((NULL == d) || (NULL == d->value)) {
return false;
}
@@ -237,21 +240,21 @@ int getdef_num (const char *item, int dflt)
def_load ();
}
d = def_find (item);
d = def_find (item, NULL);
if ((NULL == d) || (NULL == d->value)) {
return dflt;
}
if ( (getlong (d->value, &val) == 0)
if ( (getlong(d->value, &val) == -1)
|| (val > INT_MAX)
|| (val < INT_MIN)) {
|| (val < -1)) {
fprintf (shadow_logfd,
_("configuration error - cannot parse %s value: '%s'"),
item, d->value);
return dflt;
}
return (int) val;
return val;
}
@@ -272,12 +275,12 @@ unsigned int getdef_unum (const char *item, unsigned int dflt)
def_load ();
}
d = def_find (item);
d = def_find (item, NULL);
if ((NULL == d) || (NULL == d->value)) {
return dflt;
}
if ( (getlong (d->value, &val) == 0)
if ( (getlong(d->value, &val) == -1)
|| (val < 0)
|| (val > INT_MAX)) {
fprintf (shadow_logfd,
@@ -286,7 +289,7 @@ unsigned int getdef_unum (const char *item, unsigned int dflt)
return dflt;
}
return (unsigned int) val;
return val;
}
@@ -307,12 +310,12 @@ long getdef_long (const char *item, long dflt)
def_load ();
}
d = def_find (item);
d = def_find (item, NULL);
if ((NULL == d) || (NULL == d->value)) {
return dflt;
}
if (getlong (d->value, &val) == 0) {
if (getlong(d->value, &val) == -1 || val < -1) {
fprintf (shadow_logfd,
_("configuration error - cannot parse %s value: '%s'"),
item, d->value);
@@ -339,12 +342,12 @@ unsigned long getdef_ulong (const char *item, unsigned long dflt)
def_load ();
}
d = def_find (item);
d = def_find (item, NULL);
if ((NULL == d) || (NULL == d->value)) {
return dflt;
}
if (getulong (d->value, &val) == 0) {
if (getulong(d->value, &val) == -1) {
fprintf (shadow_logfd,
_("configuration error - cannot parse %s value: '%s'"),
item, d->value);
@@ -359,7 +362,7 @@ unsigned long getdef_ulong (const char *item, unsigned long dflt)
* (also used when loading the initial defaults)
*/
int putdef_str (const char *name, const char *value)
int putdef_str (const char *name, const char *value, const char *srcfile)
{
struct itemdef *d;
char *cp;
@@ -372,10 +375,9 @@ int putdef_str (const char *name, const char *value)
* Locate the slot to save the value. If this parameter
* is unknown then "def_find" will print an err message.
*/
d = def_find (name);
if (NULL == d) {
d = def_find (name, srcfile);
if (NULL == d)
return -1;
}
/*
* Save off the value.
@@ -399,9 +401,12 @@ int putdef_str (const char *name, const char *value)
*
* Search through a table of configurable items to locate the
* specified configuration option.
*
* If srcfile is not NULL, and the item is not found, then report an error saying
* the unknown item was used in this file.
*/
static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *name)
static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *name, const char *srcfile)
{
struct itemdef *ptr;
@@ -427,10 +432,11 @@ static /*@observer@*/ /*@null@*/struct itemdef *def_find (const char *name)
fprintf (shadow_logfd,
_("configuration error - unknown item '%s' (notify administrator)\n"),
name);
SYSLOG ((LOG_CRIT, "unknown configuration item `%s'", name));
if (srcfile != NULL)
SYSLOG ((LOG_CRIT, "shadow: unknown configuration item '%s' in '%s'", name, srcfile));
out:
return (struct itemdef *) NULL;
return NULL;
}
/*
@@ -442,21 +448,12 @@ out:
void setdef_config_file (const char* file)
{
#ifdef USE_ECONF
size_t len;
char* cp;
char *cp;
len = strlen(file) + strlen(sysconfdir) + 2;
cp = malloc(len);
if (cp == NULL)
exit (13);
snprintf(cp, len, "%s/%s", file, sysconfdir);
xasprintf(&cp, "%s/%s", file, sysconfdir);
sysconfdir = cp;
#ifdef VENDORDIR
len = strlen(file) + strlen(vendordir) + 2;
cp = malloc(len);
if (cp == NULL)
exit (13);
snprintf(cp, len, "%s/%s", file, vendordir);
xasprintf(&cp, "%s/%s", file, vendordir);
vendordir = cp;
#endif
#else
@@ -470,18 +467,13 @@ void setdef_config_file (const char* file)
* Loads the user-configured options from the default configuration file
*/
#ifdef USE_ECONF
static void def_load (void)
{
#ifdef USE_ECONF
econf_file *defs_file = NULL;
econf_err error;
char **keys;
size_t key_number;
#else
int i;
FILE *fp;
char buf[1024], *name, *value, *s;
#endif
/*
* Set the initialized flag.
@@ -489,8 +481,6 @@ static void def_load (void)
*/
def_loaded = true;
#ifdef USE_ECONF
error = econf_readDirs (&defs_file, vendordir, sysconfdir, "login", "defs", " \t", "#");
if (error) {
if (error == ECONF_NOFILE)
@@ -510,7 +500,12 @@ static void def_load (void)
for (size_t i = 0; i < key_number; i++) {
char *value;
econf_getStringValue(defs_file, NULL, keys[i], &value);
error = econf_getStringValue(defs_file, NULL, keys[i], &value);
if (error) {
SYSLOG ((LOG_CRIT, "failed reading key %zu from econf [%s]",
i, econf_errString(error)));
exit (EXIT_FAILURE);
}
/*
* Store the value in def_table.
@@ -519,12 +514,27 @@ static void def_load (void)
* The error was already reported to the user and to
* syslog. The tools will just use their default values.
*/
(void)putdef_str (keys[i], value);
(void)putdef_str (keys[i], value, econf_getPath(defs_file));
free(value);
}
econf_free (keys);
econf_free (defs_file);
#else
}
#else /* USE_ECONF */
static void def_load (void)
{
int i;
FILE *fp;
char buf[1024], *name, *value, *s;
/*
* Set the initialized flag.
* (do it early to prevent recursion in putdef_str())
*/
def_loaded = true;
/*
* Open the configuration definitions file.
*/
@@ -542,12 +552,12 @@ static void def_load (void)
/*
* Go through all of the lines in the file.
*/
while (fgets (buf, (int) sizeof (buf), fp) != NULL) {
while (fgets (buf, sizeof (buf), fp) != NULL) {
/*
* Trim trailing whitespace.
*/
for (i = (int) strlen (buf) - 1; i >= 0; --i) {
for (i = (ptrdiff_t) strlen (buf) - 1; i >= 0; --i) {
if (!isspace (buf[i])) {
break;
}
@@ -577,7 +587,7 @@ static void def_load (void)
* The error was already reported to the user and to
* syslog. The tools will just use their default values.
*/
(void)putdef_str (name, value);
(void)putdef_str (name, value, def_fname);
}
if (ferror (fp) != 0) {
@@ -588,8 +598,8 @@ static void def_load (void)
}
(void) fclose (fp);
#endif
}
#endif /* USE_ECONF */
#ifdef CKDEFS
@@ -602,7 +612,7 @@ int main (int argc, char **argv)
def_load ();
for (i = 0; i < NUMDEFS; ++i) {
d = def_find (def_table[i].name);
d = def_find (def_table[i].name, NULL);
if (NULL == d) {
printf ("error - lookup '%s' failed\n",
def_table[i].name);
+1 -1
View File
@@ -16,7 +16,7 @@ extern int getdef_num (const char *, int);
extern unsigned long getdef_ulong (const char *, unsigned long);
extern unsigned int getdef_unum (const char *, unsigned int);
extern /*@observer@*/ /*@null@*/const char *getdef_str (const char *);
extern int putdef_str (const char *, const char *);
extern int putdef_str (const char *, const char *, const char *);
extern void setdef_config_file (const char* file);
/* default UMASK value if not specified in /etc/login.defs */
+43
View File
@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2000 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <stdlib.h>
#include <errno.h>
#include <grp.h>
#include "prototypes.h"
/*
* getgr_nam_gid - Return a pointer to the group specified by a string.
* The string may be a valid GID or a valid groupname.
* If the group does not exist on the system, NULL is returned.
*/
extern /*@only@*//*@null@*/struct group *getgr_nam_gid (/*@null@*/const char *grname)
{
long long gid;
char *endptr;
if (NULL == grname) {
return NULL;
}
errno = 0;
gid = strtoll(grname, &endptr, 10);
if ( ('\0' != *grname)
&& ('\0' == *endptr)
&& (0 == errno)
&& (/*@+longintegral@*/gid == (gid_t)gid)/*@=longintegral@*/) {
return xgetgrgid (gid);
}
return xgetgrnam (grname);
}
+11 -11
View File
@@ -4,33 +4,33 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <stdlib.h>
#include <errno.h>
#include "prototypes.h"
/*
* getlong - extract a long integer provided by the numstr string in *result
*
* It supports decimal, hexadecimal or octal representations.
*
* Returns 0 on failure, 1 on success.
*/
int getlong (const char *numstr, /*@out@*/long int *result)
int
getlong(const char *restrict numstr, long *restrict result)
{
long val;
char *endptr;
char *endptr;
long val;
errno = 0;
val = strtol (numstr, &endptr, 0);
if (('\0' == *numstr) || ('\0' != *endptr) || (ERANGE == errno)) {
return 0;
}
val = strtol(numstr, &endptr, 0);
if (('\0' == *numstr) || ('\0' != *endptr) || (0 != errno))
return -1;
*result = val;
return 1;
return 0;
}
+94
View File
@@ -0,0 +1,94 @@
/*
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id: $"
#include <ctype.h>
#include <stdlib.h>
#include "atoi/strtou_noneg.h"
#include "defines.h"
#include "prototypes.h"
/*
* Parse a range and indicate if the range is valid.
* Valid ranges are in the form:
* <long> -> min=max=long has_min has_max
* -<long> -> max=long !has_min has_max
* <long>- -> min=long has_min !has_max
* <long1>-<long2> -> min=long1 max=long2 has_min has_max
*/
int
getrange(const char *range,
unsigned long *min, bool *has_min,
unsigned long *max, bool *has_max)
{
char *endptr;
unsigned long n;
if (NULL == range)
return -1;
if ('-' == range[0]) {
if (!isdigit(range[1]))
return -1;
errno = 0;
n = strtoul_noneg(&range[1], &endptr, 10);
if (('\0' != *endptr) || (0 != errno))
return -1;
/* -<long> */
*has_min = false;
*has_max = true;
*max = n;
} else {
errno = 0;
n = strtoul_noneg(range, &endptr, 10);
if (endptr == range || 0 != errno)
return -1;
switch (*endptr) {
case '\0':
/* <long> */
*has_min = true;
*has_max = true;
*min = n;
*max = n;
break;
case '-':
endptr++;
if ('\0' == *endptr) {
/* <long>- */
*has_min = true;
*has_max = false;
*min = n;
} else if (!isdigit (*endptr)) {
return -1;
} else {
*has_min = true;
*min = n;
errno = 0;
n = strtoul_noneg(endptr, &endptr, 10);
if ('\0' != *endptr || 0 != errno)
return -1;
/* <long>-<long> */
*has_max = true;
*max = n;
}
break;
default:
return -1;
}
}
return 0;
}
+69
View File
@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2017, Chris Lamb
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include "atoi/strtou_noneg.h"
#include "defines.h"
#include "prototypes.h"
#include "shadowlog.h"
/*
* gettime() returns the time as the number of seconds since the Epoch
*
* Like time(), gettime() returns the time as the number of seconds since the
* Epoch, 1970-01-01 00:00:00 +0000 (UTC), except that if the SOURCE_DATE_EPOCH
* environment variable is exported it will use that instead.
*/
/*@observer@*/time_t gettime (void)
{
char *endptr;
char *source_date_epoch;
time_t fallback;
unsigned long long epoch;
FILE *shadow_logfd = log_get_logfd();
fallback = time (NULL);
source_date_epoch = shadow_getenv ("SOURCE_DATE_EPOCH");
if (!source_date_epoch)
return fallback;
errno = 0;
epoch = strtoull_noneg(source_date_epoch, &endptr, 10);
if (errno != 0) {
fprintf (shadow_logfd,
_("Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n"),
strerror(errno));
} else if (endptr == source_date_epoch) {
fprintf (shadow_logfd,
_("Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n"),
endptr);
} else if (*endptr != '\0') {
fprintf (shadow_logfd,
_("Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n"),
endptr);
} else if (epoch > ULONG_MAX) {
fprintf (shadow_logfd,
_("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to %lu but was found to be: %llu\n"),
ULONG_MAX, epoch);
} else if ((time_t)epoch > fallback) {
fprintf (shadow_logfd,
_("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to the current time (%lu) but was found to be: %llu\n"),
fallback, epoch);
} else {
/* Valid */
return epoch;
}
return fallback;
}
+12 -14
View File
@@ -4,36 +4,34 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id: getlong.c 2763 2009-04-23 09:57:03Z nekral-guest $"
#include <stdlib.h>
#include <errno.h>
#include "atoi/strtou_noneg.h"
#include "prototypes.h"
/*
* getulong - extract an unsigned long integer provided by the numstr string in *result
*
* It supports decimal, hexadecimal or octal representations.
*
* Returns 0 on failure, 1 on success.
*/
int getulong (const char *numstr, /*@out@*/unsigned long int *result)
int
getulong(const char *restrict numstr, unsigned long *restrict result)
{
unsigned long int val;
char *endptr;
char *endptr;
unsigned long val;
errno = 0;
val = strtoul (numstr, &endptr, 0);
if ( ('\0' == *numstr)
|| ('\0' != *endptr)
|| (ERANGE == errno)
) {
return 0;
}
val = strtoul_noneg(numstr, &endptr, 0);
if (('\0' == *numstr) || ('\0' != *endptr) || (0 != errno))
return -1;
*result = val;
return 1;
return 0;
}
+35 -30
View File
@@ -15,12 +15,14 @@
#include <assert.h>
#include <stdio.h>
#include "alloc.h"
#include "prototypes.h"
#include "defines.h"
#include "commonio.h"
#include "getdef.h"
#include "groupio.h"
static /*@null@*/struct commonio_entry *merge_group_entries (
/*@null@*/ /*@returned@*/struct commonio_entry *gr1,
/*@null@*/struct commonio_entry *gr2);
@@ -34,7 +36,8 @@ static /*@null@*/ /*@only@*/void *group_dup (const void *ent)
return __gr_dup (gr);
}
static void group_free (/*@out@*/ /*@only@*/void *ent)
static void
group_free(/*@only@*/void *ent)
{
struct group *gr = ent;
@@ -50,7 +53,7 @@ static const char *group_getname (const void *ent)
static void *group_parse (const char *line)
{
return (void *) sgetgrent (line);
return sgetgrent (line);
}
static int group_put (const void *ent, FILE * file)
@@ -159,7 +162,7 @@ int gr_open (int mode)
int gr_update (const struct group *gr)
{
return commonio_update (&group_db, (const void *) gr);
return commonio_update (&group_db, gr);
}
int gr_remove (const char *name)
@@ -209,17 +212,25 @@ void __gr_del_entry (const struct commonio_entry *ent)
static int gr_cmp (const void *p1, const void *p2)
{
const struct commonio_entry *const *ce1;
const struct commonio_entry *const *ce2;
const struct group *g1, *g2;
gid_t u1, u2;
if ((*(struct commonio_entry **) p1)->eptr == NULL) {
ce1 = p1;
g1 = (*ce1)->eptr;
if (g1 == NULL) {
return 1;
}
if ((*(struct commonio_entry **) p2)->eptr == NULL) {
ce2 = p2;
g2 = (*ce2)->eptr;
if (g2 == NULL) {
return -1;
}
u1 = ((struct group *) (*(struct commonio_entry **) p1)->eptr)->gr_gid;
u2 = ((struct group *) (*(struct commonio_entry **) p2)->eptr)->gr_gid;
u1 = g1->gr_gid;
u2 = g2->gr_gid;
if (u1 < u2) {
return -1;
@@ -247,8 +258,8 @@ static int group_open_hook (void)
for (gr1 = group_db.head; NULL != gr1; gr1 = gr1->next) {
for (gr2 = gr1->next; NULL != gr2; gr2 = gr2->next) {
struct group *g1 = (struct group *)gr1->eptr;
struct group *g2 = (struct group *)gr2->eptr;
struct group *g1 = gr1->eptr;
struct group *g2 = gr2->eptr;
if (NULL != g1 &&
NULL != g2 &&
0 == strcmp (g1->gr_name, g2->gr_name) &&
@@ -291,32 +302,28 @@ static /*@null@*/struct commonio_entry *merge_group_entries (
/*@null@*/ /*@returned@*/struct commonio_entry *gr1,
/*@null@*/struct commonio_entry *gr2)
{
struct group *gptr1;
struct group *gptr2;
char **new_members;
size_t members = 0;
char *new_line;
size_t new_line_len, i;
char *new_line;
char **new_members;
size_t i;
size_t members = 0;
struct group *gptr1;
struct group *gptr2;
if (NULL == gr2 || NULL == gr1) {
errno = EINVAL;
return NULL;
}
gptr1 = (struct group *)gr1->eptr;
gptr2 = (struct group *)gr2->eptr;
gptr1 = gr1->eptr;
gptr2 = gr2->eptr;
if (NULL == gptr2 || NULL == gptr1) {
errno = EINVAL;
return NULL;
}
/* Concatenate the 2 lines */
new_line_len = strlen (gr1->line) + strlen (gr2->line) +1;
new_line = (char *)malloc (new_line_len + 1);
if (NULL == new_line) {
errno = ENOMEM;
if (asprintf(&new_line, "%s\n%s", gr1->line, gr2->line) == -1)
return NULL;
}
snprintf(new_line, new_line_len + 1, "%s\n%s", gr1->line, gr2->line);
/* Concatenate the 2 list of members */
for (i=0; NULL != gptr1->gr_mem[i]; i++);
@@ -333,10 +340,9 @@ static /*@null@*/struct commonio_entry *merge_group_entries (
members++;
}
}
new_members = (char **)calloc ( (members+1), sizeof(char*) );
new_members = CALLOC (members + 1, char *);
if (NULL == new_members) {
free (new_line);
errno = ENOMEM;
free(new_line);
return NULL;
}
for (i=0; NULL != gptr1->gr_mem[i]; i++) {
@@ -377,7 +383,7 @@ static int split_groups (unsigned int max_members)
struct commonio_entry *gr;
for (gr = group_db.head; NULL != gr; gr = gr->next) {
struct group *gptr = (struct group *)gr->eptr;
struct group *gptr = gr->eptr;
struct commonio_entry *new;
struct group *new_gptr;
unsigned int members = 0;
@@ -395,9 +401,8 @@ static int split_groups (unsigned int max_members)
continue;
}
new = (struct commonio_entry *) malloc (sizeof *new);
new = MALLOC(1, struct commonio_entry);
if (NULL == new) {
errno = ENOMEM;
return 0;
}
new->eptr = group_dup(gr->eptr);
@@ -406,7 +411,7 @@ static int split_groups (unsigned int max_members)
errno = ENOMEM;
return 0;
}
new_gptr = (struct group *)new->eptr;
new_gptr = new->eptr;
new->line = NULL;
new->changed = true;
+6 -34
View File
@@ -12,6 +12,8 @@
#ident "$Id$"
#include "alloc.h"
#include "memzero.h"
#include "prototypes.h"
#include "defines.h"
#include "groupio.h"
@@ -21,12 +23,11 @@
struct group *gr;
int i;
gr = (struct group *) malloc (sizeof *gr);
gr = CALLOC(1, struct group);
if (NULL == gr) {
return NULL;
}
/* The libc might define other fields. They won't be copied. */
memset (gr, 0, sizeof *gr);
gr->gr_gid = grent->gr_gid;
/*@-mustfreeonly@*/
gr->gr_name = strdup (grent->gr_name);
@@ -46,7 +47,7 @@
for (i = 0; grent->gr_mem[i]; i++);
/*@-mustfreeonly@*/
gr->gr_mem = (char **) malloc ((i + 1) * sizeof (char *));
gr->gr_mem = MALLOC(i + 1, char *);
/*@=mustfreeonly@*/
if (NULL == gr->gr_mem) {
gr_free(gr);
@@ -76,7 +77,8 @@ void gr_free_members (struct group *grent)
}
}
void gr_free (/*@out@*/ /*@only@*/struct group *grent)
void
gr_free(/*@only@*/struct group *grent)
{
free (grent->gr_name);
if (NULL != grent->gr_passwd) {
@@ -86,33 +88,3 @@ void gr_free (/*@out@*/ /*@only@*/struct group *grent)
gr_free_members(grent);
free (grent);
}
bool gr_append_member(struct group *grp, char *member)
{
int i;
if (NULL == grp->gr_mem || grp->gr_mem[0] == NULL) {
grp->gr_mem = (char **)malloc(2 * sizeof(char *));
if (!grp->gr_mem) {
return false;
}
grp->gr_mem[0] = strdup(member);
if (!grp->gr_mem[0]) {
return false;
}
grp->gr_mem[1] = NULL;
return true;
}
for (i = 0; grp->gr_mem[i]; i++) ;
grp->gr_mem = realloc(grp->gr_mem, (i + 2) * sizeof(char *));
if (NULL == grp->gr_mem) {
return false;
}
grp->gr_mem[i] = strdup(member);
if (NULL == grp->gr_mem[i]) {
return false;
}
grp->gr_mem[i + 1] = NULL;
return true;
}
+19 -212
View File
@@ -15,8 +15,12 @@
#ident "$Id$"
#include <stdio.h>
#include <string.h>
#include "alloc.h"
#include "prototypes.h"
#include "defines.h"
static /*@null@*/FILE *shadow;
static /*@null@*//*@only@*/char **members = NULL;
static size_t nmembers = 0;
@@ -26,34 +30,6 @@ static struct sgrp sgroup;
#define FIELDS 4
#ifdef USE_NIS
static bool nis_used;
static bool nis_ignore;
static enum { native, start, middle, native2 } nis_state;
static bool nis_bound;
static char *nis_domain;
static char *nis_key;
static int nis_keylen;
static char *nis_val;
static int nis_vallen;
#define IS_NISCHAR(c) ((c)=='+')
#endif
#ifdef USE_NIS
/*
* bind_nis - bind to NIS server
*/
static int bind_nis (void)
{
if (yp_get_default_domain (&nis_domain))
return -1;
nis_bound = true;
return 0;
}
#endif
static /*@null@*/char **build_list (char *s, char **list[], size_t * nlist)
{
@@ -62,21 +38,16 @@ static /*@null@*/char **build_list (char *s, char **list[], size_t * nlist)
while (s != NULL && *s != '\0') {
size = (nelem + 1) * sizeof (ptr);
ptr = realloc (*list, size);
ptr = REALLOC(*list, size, char *);
if (NULL != ptr) {
ptr[nelem] = s;
ptr[nelem] = strsep(&s, ",");
nelem++;
*list = ptr;
*nlist = nelem;
s = strchr (s, ',');
if (NULL != s) {
*s = '\0';
s++;
}
}
}
size = (nelem + 1) * sizeof (ptr);
ptr = realloc (*list, size);
ptr = REALLOC(*list, size, char *);
if (NULL != ptr) {
ptr[nelem] = NULL;
*list = ptr;
@@ -86,9 +57,6 @@ static /*@null@*/char **build_list (char *s, char **list[], size_t * nlist)
void setsgent (void)
{
#ifdef USE_NIS
nis_state = native;
#endif
if (NULL != shadow) {
rewind (shadow);
} else {
@@ -102,7 +70,7 @@ void endsgent (void)
(void) fclose (shadow);
}
shadow = (FILE *) 0;
shadow = NULL;
}
/*@observer@*//*@null@*/struct sgrp *sgetsgent (const char *string)
@@ -116,7 +84,7 @@ void endsgent (void)
size_t len = strlen (string) + 1;
if (len > sgrbuflen) {
char *buf = (char *) realloc (sgrbuf, sizeof (char) * len);
char *buf = REALLOC(sgrbuf, len, char);
if (NULL == buf) {
return NULL;
}
@@ -124,8 +92,7 @@ void endsgent (void)
sgrbuflen = len;
}
strncpy (sgrbuf, string, len);
sgrbuf[len-1] = '\0';
strcpy (sgrbuf, string);
cp = strrchr (sgrbuf, '\n');
if (NULL != cp) {
@@ -137,30 +104,16 @@ void endsgent (void)
* all 4 of them and save the starting addresses in fields[].
*/
for (cp = sgrbuf, i = 0; (i < FIELDS) && (NULL != cp); i++) {
fields[i] = cp;
cp = strchr (cp, ':');
if (NULL != cp) {
*cp++ = '\0';
}
}
for (cp = sgrbuf, i = 0; (i < FIELDS) && (NULL != cp); i++)
fields[i] = strsep(&cp, ":");
/*
* If there was an extra field somehow, or perhaps not enough,
* the line is invalid.
*/
if ((NULL != cp) || (i != FIELDS)) {
#ifdef USE_NIS
if (!IS_NISCHAR (fields[0][0])) {
return 0;
} else {
nis_used = true;
}
#else
if (NULL != cp || i != FIELDS)
return 0;
#endif
}
sgroup.sg_name = fields[0];
sgroup.sg_passwd = fields[1];
@@ -195,7 +148,7 @@ void endsgent (void)
char *cp;
if (0 == buflen) {
buf = (char *) malloc (BUFSIZ);
buf = MALLOC(BUFSIZ, char);
if (NULL == buf) {
return NULL;
}
@@ -206,17 +159,12 @@ void endsgent (void)
return NULL;
}
#ifdef USE_NIS
while (fgetsx (buf, (int) buflen, fp) == buf)
#else
if (fgetsx (buf, (int) buflen, fp) == buf)
#endif
{
if (fgetsx(buf, buflen, fp) == buf) {
while ( ((cp = strrchr (buf, '\n')) == NULL)
&& (feof (fp) == 0)) {
size_t len;
cp = (char *) realloc (buf, buflen*2);
cp = REALLOC(buf, buflen * 2, char);
if (NULL == cp) {
return NULL;
}
@@ -234,11 +182,6 @@ void endsgent (void)
if (NULL != cp) {
*cp = '\0';
}
#ifdef USE_NIS
if (nis_ignore && IS_NISCHAR (buf[0])) {
continue;
}
#endif
return (sgetsgent (buf));
}
return NULL;
@@ -250,96 +193,10 @@ void endsgent (void)
/*@observer@*//*@null@*/struct sgrp *getsgent (void)
{
#ifdef USE_NIS
bool nis_1_group = false;
struct sgrp *val;
#endif
if (NULL == shadow) {
setsgent ();
}
#ifdef USE_NIS
again:
/*
* See if we are reading from the local file.
*/
if (nis_state == native || nis_state == native2) {
/*
* Get the next entry from the shadow group file. Return
* NULL right away if there is none.
*/
val = fgetsgent (shadow);
if (NULL == val) {
return 0;
}
/*
* If this entry began with a NIS escape character, we have
* to see if this is just a single group, or if the entire
* map is being asked for.
*/
if (IS_NISCHAR (val->sg_name[0])) {
if ('\0' != val->sg_name[1]) {
nis_1_group = true;
} else {
nis_state = start;
}
}
/*
* If this isn't a NIS group and this isn't an escape to go
* use a NIS map, it must be a regular local group.
*/
if (!nis_1_group && (nis_state != start)) {
return val;
}
/*
* If this is an escape to use an NIS map, switch over to
* that bunch of code.
*/
if (nis_state == start) {
goto again;
}
/*
* NEEDSWORK. Here we substitute pieces-parts of this entry.
*/
return 0;
} else {
if (!nis_bound) {
if (bind_nis ()) {
nis_state = native2;
goto again;
}
}
if (nis_state == start) {
if (yp_first (nis_domain, "gshadow.byname", &nis_key,
&nis_keylen, &nis_val, &nis_vallen)) {
nis_state = native2;
goto again;
}
nis_state = middle;
} else if (nis_state == middle) {
if (yp_next (nis_domain, "gshadow.byname", nis_key,
nis_keylen, &nis_key, &nis_keylen,
&nis_val, &nis_vallen)) {
nis_state = native2;
goto again;
}
}
return sgetsgent (nis_val);
}
#else
return (fgetsgent (shadow));
#endif
}
/*
@@ -350,63 +207,13 @@ void endsgent (void)
{
struct sgrp *sgrp;
#ifdef USE_NIS
static char save_name[16];
int nis_disabled = 0;
#endif
setsgent ();
#ifdef USE_NIS
if (nis_used) {
again:
/*
* Search the gshadow.byname map for this group.
*/
if (!nis_bound) {
bind_nis ();
}
if (nis_bound) {
char *cp;
if (yp_match (nis_domain, "gshadow.byname", name,
strlen (name), &nis_val,
&nis_vallen) == 0) {
cp = strchr (nis_val, '\n');
if (NULL != cp) {
*cp = '\0';
}
nis_state = middle;
sgrp = sgetsgent (nis_val);
if (NULL != sgrp) {
strcpy (save_name, sgrp->sg_name);
nis_key = save_name;
nis_keylen = strlen (save_name);
}
return sgrp;
}
}
nis_state = native2;
}
#endif
#ifdef USE_NIS
if (nis_used) {
nis_ignore = true;
nis_disabled = true;
}
#endif
while ((sgrp = getsgent ()) != (struct sgrp *) 0) {
while ((sgrp = getsgent ()) != NULL) {
if (strcmp (name, sgrp->sg_name) == 0) {
break;
}
}
#ifdef USE_NIS
nis_ignore = false;
#endif
return sgrp;
}
@@ -437,7 +244,7 @@ int putsgent (const struct sgrp *sgrp, FILE * fp)
size += strlen (sgrp->sg_mem[i]) + 1;
}
buf = malloc (size);
buf = MALLOC(size, char);
if (NULL == buf) {
return -1;
}
@@ -502,5 +309,5 @@ int putsgent (const struct sgrp *sgrp, FILE * fp)
return 0;
}
#else
extern int errno; /* warning: ANSI C forbids an empty source file */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /*} SHADOWGRP */
+80
View File
@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 1991 - 1993, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1991 - 1993, Chip Rosenthal
* SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include "defines.h"
#include "prototypes.h"
#include "getdef.h"
#include "string/sprintf.h"
/*
* hushed - determine if a user receives login messages
*
* Look in the hushed-logins file (or user's home directory) to see
* if the user is to receive the login-time messages.
*/
bool hushed (const char *username)
{
bool found;
char buf[BUFSIZ];
FILE *fp;
const char *hushfile;
struct passwd *pw;
/*
* Get the name of the file to use. If this option is not
* defined, default to a noisy login.
*/
hushfile = getdef_str ("HUSHLOGIN_FILE");
if (NULL == hushfile) {
return false;
}
pw = getpwnam (username);
if (NULL == pw) {
return false;
}
/*
* If this is not a fully rooted path then see if the
* file exists in the user's home directory.
*/
if (hushfile[0] != '/') {
SNPRINTF(buf, "%s/%s", pw->pw_dir, hushfile);
return (access (buf, F_OK) == 0);
}
/*
* If this is a fully rooted path then go through the file
* and see if this user, or its shell is in there.
*/
fp = fopen (hushfile, "r");
if (NULL == fp) {
return false;
}
for (found = false; !found && (fgets (buf, sizeof buf, fp) == buf);) {
buf[strcspn (buf, "\n")] = '\0';
found = (strcmp (buf, pw->pw_shell) == 0) ||
(strcmp (buf, pw->pw_name) == 0);
}
(void) fclose (fp);
return found;
}
+222
View File
@@ -0,0 +1,222 @@
/*
* SPDX-FileCopyrightText: 2013 Eric Biederman
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include "alloc.h"
#include "prototypes.h"
#include "string/stpeprintf.h"
#include "idmapping.h"
#if HAVE_SYS_CAPABILITY_H
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
#include "shadowlog.h"
#include "sizeof.h"
struct map_range *get_map_ranges(int ranges, int argc, char **argv)
{
struct map_range *mappings, *mapping;
int idx, argidx;
if (ranges < 0 || argc < 0) {
fprintf(log_get_logfd(), "%s: error calculating number of arguments\n", log_get_progname());
return NULL;
}
if (ranges * 3 != argc) {
fprintf(log_get_logfd(), "%s: ranges: %u is wrong for argc: %d\n", log_get_progname(), ranges, argc);
return NULL;
}
mappings = CALLOC(ranges, struct map_range);
if (!mappings) {
fprintf(log_get_logfd(), _( "%s: Memory allocation failure\n"),
log_get_progname());
exit(EXIT_FAILURE);
}
/* Gather up the ranges from the command line */
mapping = mappings;
for (idx = 0, argidx = 0; idx < ranges; idx++, argidx += 3, mapping++) {
if (getulong(argv[argidx + 0], &mapping->upper) == -1) {
free(mappings);
return NULL;
}
if (getulong(argv[argidx + 1], &mapping->lower) == -1) {
free(mappings);
return NULL;
}
if (getulong(argv[argidx + 2], &mapping->count) == -1) {
free(mappings);
return NULL;
}
if (ULONG_MAX - mapping->upper <= mapping->count || ULONG_MAX - mapping->lower <= mapping->count) {
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
exit(EXIT_FAILURE);
}
if (mapping->upper > UINT_MAX ||
mapping->lower > UINT_MAX ||
mapping->count > UINT_MAX) {
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
exit(EXIT_FAILURE);
}
if (mapping->lower + mapping->count > UINT_MAX ||
mapping->upper + mapping->count > UINT_MAX) {
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
exit(EXIT_FAILURE);
}
if (mapping->lower + mapping->count < mapping->lower ||
mapping->upper + mapping->count < mapping->upper) {
/* this one really shouldn't be possible given previous checks */
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
exit(EXIT_FAILURE);
}
}
return mappings;
}
/* Number of ascii digits needed to print any unsigned long in decimal.
* There are approximately 10 bits for every 3 decimal digits.
* So from bits to digits the formula is roundup((Number of bits)/10) * 3.
* For common sizes of integers this works out to:
* 2bytes --> 6 ascii estimate -> 65536 (5 real)
* 4bytes --> 12 ascii estimated -> 4294967296 (10 real)
* 8bytes --> 21 ascii estimated -> 18446744073709551616 (20 real)
* 16bytes --> 39 ascii estimated -> 340282366920938463463374607431768211456 (39 real)
*/
#define ULONG_DIGITS (((WIDTHOF(unsigned long) + 9)/10)*3)
#if HAVE_SYS_CAPABILITY_H
static inline bool maps_lower_root(int cap, int ranges, const struct map_range *mappings)
{
int idx;
const struct map_range *mapping;
if (cap != CAP_SETUID)
return false;
mapping = mappings;
for (idx = 0; idx < ranges; idx++, mapping++) {
if (mapping->lower == 0)
return true;
}
return false;
}
#endif
/*
* The ruid refers to the caller's uid and is used to reset the effective uid
* back to the callers real uid.
* This clutch mainly exists for setuid-based new{g,u}idmap binaries that are
* called in contexts where all capabilities other than the necessary
* CAP_SET{G,U}ID capabilities are dropped. Since the kernel will require
* assurance that the caller holds CAP_SYS_ADMIN over the target user namespace
* the only way it can confirm is in this case is if the effective uid is
* equivalent to the uid owning the target user namespace.
* Note, we only support this when a) new{g,u}idmap is not called by root and
* b) if the caller's uid and the uid retrieved via system appropriate means
* (shadow file or other) are identical. Specifically, this does not support
* when the root user calls the new{g,u}idmap binary for an unprivileged user.
* If this is wanted: use file capabilities!
*/
void write_mapping(int proc_dir_fd, int ranges, const struct map_range *mappings,
const char *map_file, uid_t ruid)
{
int idx;
const struct map_range *mapping;
size_t bufsize;
char *buf, *pos, *end;
int fd;
#if HAVE_SYS_CAPABILITY_H
int cap;
struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0};
struct __user_cap_data_struct data[2] = {{0}};
if (strcmp(map_file, "uid_map") == 0) {
cap = CAP_SETUID;
} else if (strcmp(map_file, "gid_map") == 0) {
cap = CAP_SETGID;
} else {
fprintf(log_get_logfd(), _("%s: Invalid map file %s specified\n"), log_get_progname(), map_file);
exit(EXIT_FAILURE);
}
/* Align setuid- and fscaps-based new{g,u}idmap behavior. */
if (geteuid() == 0 && geteuid() != ruid) {
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
fprintf(log_get_logfd(), _("%s: Could not prctl(PR_SET_KEEPCAPS)\n"), log_get_progname());
exit(EXIT_FAILURE);
}
if (seteuid(ruid) < 0) {
fprintf(log_get_logfd(), _("%s: Could not seteuid to %d\n"), log_get_progname(), ruid);
exit(EXIT_FAILURE);
}
}
/* Lockdown new{g,u}idmap by dropping all unneeded capabilities. */
bzero(data, sizeof(data));
data[0].effective = CAP_TO_MASK(cap);
/*
* When uid 0 from the ancestor userns is supposed to be mapped into
* the child userns we need to retain CAP_SETFCAP.
*/
if (maps_lower_root(cap, ranges, mappings))
data[0].effective |= CAP_TO_MASK(CAP_SETFCAP);
data[0].permitted = data[0].effective;
if (capset(&hdr, data) < 0) {
fprintf(log_get_logfd(), _("%s: Could not set caps\n"), log_get_progname());
exit(EXIT_FAILURE);
}
#endif
bufsize = (ULONG_DIGITS + 1) * 3 * ranges + 1;
pos = buf = XMALLOC(bufsize, char);
end = buf + bufsize;
/* Build the mapping command */
mapping = mappings;
for (idx = 0; idx < ranges; idx++, mapping++) {
/* Append this range to the string that will be written */
pos = stpeprintf(pos, end, "%lu %lu %lu\n",
mapping->upper,
mapping->lower,
mapping->count);
}
if (pos == end || pos == NULL) {
fprintf(log_get_logfd(), _("%s: stpeprintf failed!\n"), log_get_progname());
exit(EXIT_FAILURE);
}
/* Write the mapping to the mapping file */
fd = openat(proc_dir_fd, map_file, O_WRONLY);
if (fd < 0) {
fprintf(log_get_logfd(), _("%s: open of %s failed: %s\n"),
log_get_progname(), map_file, strerror(errno));
exit(EXIT_FAILURE);
}
if (write_full(fd, buf, pos - buf) == -1) {
fprintf(log_get_logfd(), _("%s: write to %s failed: %s\n"),
log_get_progname(), map_file, strerror(errno));
exit(EXIT_FAILURE);
}
if (close(fd) != 0 && errno != EINTR) {
fprintf(log_get_logfd(), _("%s: closing %s failed: %s\n"),
log_get_progname(), map_file, strerror(errno));
exit(EXIT_FAILURE);
}
free(buf);
}
+23
View File
@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2013 Eric Biederman
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _IDMAPPING_H_
#define _IDMAPPING_H_
#include <sys/types.h>
struct map_range {
unsigned long upper; /* first ID inside the namespace */
unsigned long lower; /* first ID outside the namespace */
unsigned long count; /* Length of the inside and outside ranges */
};
extern struct map_range *get_map_ranges(int ranges, int argc, char **argv);
extern void write_mapping(int proc_dir_fd, int ranges,
const struct map_range *mappings, const char *map_file, uid_t ruid);
#endif /* _ID_MAPPING_H_ */
+105
View File
@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1997, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2001 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Extracted from age.c and made part of libshadow.a - may be useful
* in other shadow-aware programs. --marekm
*/
#include <config.h>
#include <sys/types.h>
#include <pwd.h>
#include <time.h>
#include "adds.h"
#include "defines.h"
#include "prototypes.h"
#ident "$Id$"
/*
* isexpired - determine if account is expired yet
*
* isexpired calculates the expiration date based on the
* password expiration criteria.
*
* Return value:
* 0: The password is still valid
* 1: The password has expired, it must be changed
* 2: The password has expired since a long time and the account is
* now disabled. (password cannot be changed)
* 3: The account has expired
*/
int isexpired (const struct passwd *pw, /*@null@*/const struct spwd *sp)
{
long now;
now = time(NULL) / DAY;
if (NULL == sp) {
return 0;
}
/*
* Quick and easy - there is an expired account field
* along with an inactive account field. Do the expired
* one first since it is worse.
*/
if ((sp->sp_expire > 0) && (now >= sp->sp_expire)) {
return 3;
}
/*
* Last changed date 1970-01-01 (not very likely) means that
* the password must be changed on next login (passwd -e).
*
* The check for "x" is a workaround for RedHat NYS libc bug -
* if /etc/shadow doesn't exist, getspnam() still succeeds and
* returns sp_lstchg==0 (must change password) instead of -1!
*/
if ( (0 == sp->sp_lstchg)
&& (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0)) {
return 1;
}
if ( (sp->sp_lstchg > 0)
&& (sp->sp_max >= 0)
&& (sp->sp_inact >= 0)
&& (now >= addsl(sp->sp_lstchg, sp->sp_max, sp->sp_inact)))
{
return 2;
}
/*
* The last and max fields must be present for an account
* to have an expired password. A maximum of >10000 days
* is considered to be infinite.
*/
if ( (-1 == sp->sp_lstchg)
|| (-1 == sp->sp_max)
|| (sp->sp_max >= 10000)) {
return 0;
}
/*
* Calculate today's day and the day on which the password
* is going to expire. If that date has already passed,
* the password has expired.
*/
if (now >= addsl(sp->sp_lstchg, sp->sp_max))
return 1;
return 0;
}
+533
View File
@@ -0,0 +1,533 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Separated from setup.c. --marekm
* Resource limits thanks to Cristian Gafton.
* Enhancements of resource limit code by Thomas Orgis <thomas@orgis.org>
*/
#include <config.h>
#ifndef USE_PAM
#ident "$Id$"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <ctype.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
#include "getdef.h"
#include "shadowlog.h"
#include <sys/resource.h>
#include "memzero.h"
#ifndef LIMITS_FILE
#define LIMITS_FILE "/etc/limits"
#endif
#define LOGIN_ERROR_RLIMIT 1
#define LOGIN_ERROR_LOGIN 2
/* Set a limit on a resource */
/*
* rlimit - RLIMIT_XXXX
* value - string value to be read
* multiplier - value*multiplier is the actual limit
*/
static int setrlimit_value (unsigned int resource,
const char *value,
unsigned int multiplier)
{
char *endptr;
long l;
rlim_t limit;
struct rlimit rlim;
/* The "-" is special, not belonging to a strange negative limit.
* It is infinity, in a controlled way.
*/
if ('-' == value[0]) {
limit = RLIM_INFINITY;
}
else {
/* We cannot use getlong here because it fails when there
* is more to the value than just this number!
* Also, we are limited to base 10 here (hex numbers will not
* work with the limit string parser as is anyway)
*/
errno = 0;
l = strtol(value, &endptr, 10);
if (value == endptr || errno != 0)
return 0; // FIXME: We could instead throw an error, though.
if (__builtin_mul_overflow(l, multiplier, &limit)) {
/* FIXME: Again, silent error handling...
* Wouldn't screaming make more sense?
*/
return 0;
}
}
rlim.rlim_cur = limit;
rlim.rlim_max = limit;
if (setrlimit (resource, &rlim) != 0) {
return LOGIN_ERROR_RLIMIT;
}
return 0;
}
static int set_prio (const char *value)
{
long prio;
if ( (getlong(value, &prio) == -1)
|| (prio != (int) prio)) {
return 0;
}
if (setpriority (PRIO_PROCESS, 0, prio) != 0) {
return LOGIN_ERROR_RLIMIT;
}
return 0;
}
static int set_umask (const char *value)
{
unsigned long mask;
if ( (getulong(value, &mask) == -1)
|| (mask != (mode_t) mask)) {
return 0;
}
(void) umask (mask);
return 0;
}
/* Counts the number of user logins and check against the limit */
static int check_logins (const char *name, const char *maxlogins)
{
unsigned long limit, count;
if (getulong(maxlogins, &limit) == -1) {
return 0;
}
if (0 == limit) { /* maximum 0 logins ? */
SYSLOG ((LOG_WARN, "No logins allowed for `%s'\n", name));
return LOGIN_ERROR_LOGIN;
}
count = active_sessions_count(name, limit);
if (count > limit) {
SYSLOG ((LOG_WARN,
"Too many logins (max %lu) for %s\n",
limit, name));
return LOGIN_ERROR_LOGIN;
}
return 0;
}
/* Function setup_user_limits - checks/set limits for the current login
* Original idea from Joel Katz's lshell. Ported to shadow-login
* by Cristian Gafton - gafton@sorosis.ro
*
* We are passed a string of the form ('BASH' constants for ulimit)
* [Aa][Cc][Dd][Ff][Mm][Nn][Rr][Ss][Tt][Uu][Ll][Pp][Ii][Oo]
* (eg. 'C2F256D2048N5' or 'C2 F256 D2048 N5')
* where:
* [Aa]: a = RLIMIT_AS max address space (KB)
* [Cc]: c = RLIMIT_CORE max core file size (KB)
* [Dd]: d = RLIMIT_DATA max data size (KB)
* [Ff]: f = RLIMIT_FSIZE max file size (KB)
* [Ii]: i = RLIMIT_NICE max nice value (0..39 translates to 20..-19)
* [Kk]: k = file creation masK (umask)
* [Ll]: l = max number of logins for this user
* [Mm]: m = RLIMIT_MEMLOCK max locked-in-memory address space (KB)
* [Nn]: n = RLIMIT_NOFILE max number of open files
* [Oo]: o = RLIMIT_RTPRIO max real time priority (linux/sched.h 0..MAX_RT_PRIO)
* [Pp]: p = process priority -20..20 (negative = high, positive = low)
* [Rr]: r = RLIMIT_RSS max resident set size (KB)
* [Ss]: s = RLIMIT_STACK max stack size (KB)
* [Tt]: t = RLIMIT_CPU max CPU time (MIN)
* [Uu]: u = RLIMIT_NPROC max number of processes
*
* NOTE: Remember to extend the "no-limits" string below when adding a new
* limit...
*
* Return value:
* 0 = okay, of course
* LOGIN_ERROR_RLIMIT = error setting some RLIMIT
* LOGIN_ERROR_LOGIN = error - too many logins for this user
*
* buf - the limits string
* name - the username
*/
static int do_user_limits (const char *buf, const char *name)
{
const char *pp;
int retval = 0;
bool reported = false;
pp = buf;
/* Skip leading whitespace. */
while ((' ' == *pp) || ('\t' == *pp)) {
pp++;
}
/* The special limit string "-" results in no limit for all known
* limits.
* We achieve that by parsing a full limit string, parts of it
* being ignored if a limit type is not known to the system.
* Though, there will be complaining for unknown limit types.
*/
if (strcmp (pp, "-") == 0) {
/* Remember to extend this, too, when adding new limits!
* Oh... but "unlimited" does not make sense for umask,
* or does it? (K-)
*/
pp = "A- C- D- F- I- L- M- N- O- P- R- S- T- U-";
}
while ('\0' != *pp) {
switch (*pp++) {
case 'a':
case 'A':
/* RLIMIT_AS - max address space (KB) */
retval |= setrlimit_value (RLIMIT_AS, pp, 1024);
break;
case 'c':
case 'C':
/* RLIMIT_CORE - max core file size (KB) */
retval |= setrlimit_value (RLIMIT_CORE, pp, 1024);
break;
case 'd':
case 'D':
/* RLIMIT_DATA - max data size (KB) */
retval |= setrlimit_value (RLIMIT_DATA, pp, 1024);
break;
case 'f':
case 'F':
/* RLIMIT_FSIZE - Maximum filesize (KB) */
retval |= setrlimit_value (RLIMIT_FSIZE, pp, 1024);
break;
#ifdef RLIMIT_NICE
case 'i':
case 'I':
/* RLIMIT_NICE - max scheduling priority (0..39) */
retval |= setrlimit_value (RLIMIT_NICE, pp, 1);
break;
#endif
case 'k':
case 'K':
retval |= set_umask (pp);
break;
case 'l':
case 'L':
/* LIMIT the number of concurrent logins */
retval |= check_logins (name, pp);
break;
#ifdef RLIMIT_MEMLOCK
case 'm':
case 'M':
/* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */
retval |= setrlimit_value (RLIMIT_MEMLOCK, pp, 1024);
break;
#endif
case 'n':
case 'N':
/* RLIMIT_NOFILE - max number of open files */
retval |= setrlimit_value (RLIMIT_NOFILE, pp, 1);
break;
#ifdef RLIMIT_RTPRIO
case 'o':
case 'O':
/* RLIMIT_RTPRIO - max real time priority (0..MAX_RT_PRIO) */
retval |= setrlimit_value (RLIMIT_RTPRIO, pp, 1);
break;
#endif
case 'p':
case 'P':
retval |= set_prio (pp);
break;
#ifdef RLIMIT_RSS
case 'r':
case 'R':
/* RLIMIT_RSS - max resident set size (KB) */
retval |= setrlimit_value (RLIMIT_RSS, pp, 1024);
break;
#endif
case 's':
case 'S':
/* RLIMIT_STACK - max stack size (KB) */
retval |= setrlimit_value (RLIMIT_STACK, pp, 1024);
break;
case 't':
case 'T':
/* RLIMIT_CPU - max CPU time (MIN) */
retval |= setrlimit_value (RLIMIT_CPU, pp, 60);
break;
#ifdef RLIMIT_NPROC
case 'u':
case 'U':
/* RLIMIT_NPROC - max number of processes */
retval |= setrlimit_value (RLIMIT_NPROC, pp, 1);
break;
#endif
default:
/* Only report invalid strings once */
/* Note: A string can be invalid just because a
* specific (theoretically valid) setting is not
* supported by this build.
* It is just a warning in syslog anyway. The line
* is still processed
*/
if (!reported) {
SYSLOG ((LOG_WARN,
"Invalid limit string: '%s'",
pp-1));
reported = true;
retval |= LOGIN_ERROR_RLIMIT;
}
}
/* After parsing one limit setting (or just complaining
* about it), one still needs to skip its argument to
* prevent a bogus warning on trying to parse that as
* limit specification.
* So, let's skip all digits, "-" and our limited set of
* whitespace.
*/
while ( isdigit (*pp)
|| ('-' == *pp)
|| (' ' == *pp)
|| ('\t' ==*pp)) {
pp++;
}
}
return retval;
}
/* Check if user uname is in the group gname.
* Can I be sure that gr_mem contains no UID as string?
* Returns true when user is in the group, false when not.
* Any error is treated as false.
*/
static bool user_in_group (const char *uname, const char *gname)
{
struct group *groupdata;
if (uname == NULL || gname == NULL) {
return false;
}
/* We are not claiming to be re-entrant!
* In case of paranoia or a multithreaded login program,
* one needs to add some mess for getgrnam_r. */
groupdata = getgrnam (gname);
if (NULL == groupdata) {
SYSLOG ((LOG_WARN, "Nonexisting group `%s' in limits file.",
gname));
return false;
}
return is_on_list (groupdata->gr_mem, uname);
}
static int setup_user_limits (const char *uname)
{
FILE *fil;
char buf[1024];
char name[1024];
char limits[1024];
char deflimits[1024];
char tempbuf[1024];
/* init things */
MEMZERO(buf);
MEMZERO(name);
MEMZERO(limits);
MEMZERO(deflimits);
MEMZERO(tempbuf);
/* start the checks */
fil = fopen (LIMITS_FILE, "r");
if (fil == NULL) {
return 0;
}
/* The limits file have the following format:
* - '#' (comment) chars only as first chars on a line;
* - username must start on first column (or *, or @group)
*
* FIXME: A better (smarter) checking should be done
*/
while (fgets (buf, 1024, fil) != NULL) {
if (('#' == buf[0]) || ('\n' == buf[0])) {
continue;
}
MEMZERO(tempbuf);
/* a valid line should have a username, then spaces,
* then limits
* we allow the format:
* username L2 D2048 R4096
* where spaces={' ',\t}. Also, we reject invalid limits.
* Imposing a limit should be done with care, so a wrong
* entry means no care anyway :-).
*
* A '-' as a limits strings means no limits
*
* The username can also be:
* '*': the default limits (only the last is taken into
* account)
* @group: the limit applies to the members of the group
*
* To clarify: The first entry with matching user name rules,
* everything after it is ignored. If there is no user entry,
* the last encountered entry for a matching group rules.
* If there is no matching group entry, the default limits rule.
*/
if (sscanf (buf, "%s%[ACDFIKLMNOPRSTUacdfiklmnoprstu0-9 \t-]",
name, tempbuf) == 2) {
if (strcmp (name, uname) == 0) {
strcpy (limits, tempbuf);
break;
} else if (strcmp (name, "*") == 0) {
strcpy (deflimits, tempbuf);
} else if (name[0] == '@') {
/* If the user is in the group, the group
* limits apply unless later a line for
* the specific user is found.
*/
if (user_in_group (uname, name+1)) {
strcpy (limits, tempbuf);
}
}
}
}
(void) fclose (fil);
if (limits[0] == '\0') {
/* no user specific limits */
if (deflimits[0] == '\0') { /* no default limits */
return 0;
}
strcpy (limits, deflimits); /* use the default limits */
}
return do_user_limits (limits, uname);
}
static void setup_usergroups (const struct passwd *info)
{
const struct group *grp;
/*
* if not root, and UID == GID, and username is the same as primary
* group name, set umask group bits to be the same as owner bits
* (examples: 022 -> 002, 077 -> 007).
*/
if ((0 != info->pw_uid) && (info->pw_uid == info->pw_gid)) {
/* local, no need for xgetgrgid */
grp = getgrgid (info->pw_gid);
if ( (NULL != grp)
&& (strcmp (info->pw_name, grp->gr_name) == 0)) {
mode_t tmpmask;
tmpmask = umask (0777);
tmpmask = (tmpmask & ~070) | ((tmpmask >> 3) & 070);
(void) umask (tmpmask);
}
}
}
/*
* set the process nice, ulimit, and umask from the password file entry
*/
void setup_limits (const struct passwd *info)
{
char *cp;
if (getdef_bool ("USERGROUPS_ENAB")) {
setup_usergroups (info);
}
/*
* See if the GECOS field contains values for NICE, UMASK or ULIMIT.
* If this feature is enabled in /etc/login.defs, we make those
* values the defaults for this login session.
*/
if (getdef_bool ("QUOTAS_ENAB")) {
if (info->pw_uid != 0) {
if ((setup_user_limits (info->pw_name) & LOGIN_ERROR_LOGIN) != 0) {
(void) fputs (_("Too many logins.\n"), log_get_logfd());
(void) sleep (2); /* XXX: Should be FAIL_DELAY */
exit (EXIT_FAILURE);
}
}
for (cp = info->pw_gecos; cp != NULL; cp = strchr (cp, ',')) {
if (',' == *cp) {
cp++;
}
if (strncmp (cp, "pri=", 4) == 0) {
long inc;
if ( (getlong(cp + 4, &inc) == 0)
&& (inc >= -20) && (inc <= 20)) {
errno = 0;
if ( (nice (inc) != -1)
|| (0 != errno)) {
continue;
}
}
/* Failed to parse or failed to nice() */
SYSLOG ((LOG_WARN,
"Can't set the nice value for user %s",
info->pw_name));
continue;
}
if (strncmp (cp, "ulimit=", 7) == 0) {
long blocks;
if ( (getlong(cp + 7, &blocks) == -1)
|| (blocks != (int) blocks)
|| (set_filesize_limit (blocks) != 0)) {
SYSLOG ((LOG_WARN,
"Can't set the ulimit for user %s",
info->pw_name));
}
continue;
}
if (strncmp (cp, "umask=", 6) == 0) {
unsigned long mask;
if ( (getulong(cp + 6, &mask) == -1)
|| (mask != (mode_t) mask)) {
SYSLOG ((LOG_WARN,
"Can't set umask value for user %s",
info->pw_name));
} else {
(void) umask (mask);
}
continue;
}
}
}
}
#else /* !USE_PAM */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* !USE_PAM */
+245
View File
@@ -0,0 +1,245 @@
/*
* SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1997, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <assert.h>
#include "alloc.h"
#include "prototypes.h"
#include "defines.h"
/*
* add_list - add a member to a list of group members
*
* the array of member names is searched for the new member
* name, and if not present it is added to a freshly allocated
* list of users.
*/
/*@only@*/char **
add_list(/*@returned@*/ /*@only@*/char **list, const char *member)
{
int i;
char **tmp;
assert (NULL != member);
assert (NULL != list);
/*
* Scan the list for the new name. Return the original list
* pointer if it is present.
*/
for (i = 0; list[i] != NULL; i++) {
if (strcmp (list[i], member) == 0) {
return list;
}
}
/*
* Allocate a new list pointer large enough to hold all the
* old entries, and the new entries as well.
*/
tmp = XMALLOC(i + 2, char *);
/*
* Copy the original list to the new list, then append the
* new member and NULL terminate the result. This new list
* is returned to the invoker.
*/
for (i = 0; list[i] != NULL; i++) {
tmp[i] = list[i];
}
tmp[i] = xstrdup (member);
tmp[i+1] = NULL;
return tmp;
}
/*
* del_list - delete a member from a list of group members
*
* the array of member names is searched for the old member
* name, and if present it is deleted from a freshly allocated
* list of users.
*/
/*@only@*/char **
del_list(/*@returned@*/ /*@only@*/char **list, const char *member)
{
int i, j;
char **tmp;
assert (NULL != member);
assert (NULL != list);
/*
* Scan the list for the old name. Return the original list
* pointer if it is not present.
*/
for (i = j = 0; list[i] != NULL; i++) {
if (strcmp (list[i], member) != 0) {
j++;
}
}
if (j == i) {
return list;
}
/*
* Allocate a new list pointer large enough to hold all the
* old entries.
*/
tmp = XMALLOC(j + 1, char *);
/*
* Copy the original list except the deleted members to the
* new list, then NULL terminate the result. This new list
* is returned to the invoker.
*/
for (i = j = 0; list[i] != NULL; i++) {
if (strcmp (list[i], member) != 0) {
tmp[j] = list[i];
j++;
}
}
tmp[j] = NULL;
return tmp;
}
/*
* Duplicate a list.
* The input list is not modified, but in order to allow the use of this
* function with list of members, the list elements are not enforced to be
* constant strings here.
*/
/*@only@*/char **
dup_list(char *const *list)
{
int i;
char **tmp;
assert (NULL != list);
for (i = 0; NULL != list[i]; i++);
tmp = XMALLOC(i + 1, char *);
i = 0;
while (NULL != *list) {
tmp[i] = xstrdup (*list);
i++;
list++;
}
tmp[i] = NULL;
return tmp;
}
/*
* Check if member is part of the input list
* The input list is not modified, but in order to allow the use of this
* function with list of members, the list elements are not enforced to be
* constant strings here.
*/
bool is_on_list (char *const *list, const char *member)
{
assert (NULL != member);
assert (NULL != list);
while (NULL != *list) {
if (strcmp (*list, member) == 0) {
return true;
}
list++;
}
return false;
}
/*
* comma_to_list - convert comma-separated list to (char *) array
*/
/*@only@*/char **comma_to_list (const char *comma)
{
char *members;
char **array;
int i;
char *cp;
char *cp2;
assert (NULL != comma);
/*
* Make a copy since we are going to be modifying the list
*/
members = xstrdup (comma);
/*
* Count the number of commas in the list
*/
for (cp = members, i = 0;; i++) {
cp2 = strchr (cp, ',');
if (NULL != cp2) {
cp = cp2 + 1;
} else {
break;
}
}
/*
* Add 2 - one for the ending NULL, the other for the last item
*/
i += 2;
/*
* Allocate the array we're going to store the pointers into.
*/
array = XMALLOC(i, char *);
/*
* Empty list is special - 0 members, not 1 empty member. --marekm
*/
if ('\0' == *members) {
*array = NULL;
free (members);
return array;
}
/*
* Now go walk that list all over again, this time building the
* array of pointers.
*/
for (cp = members, i = 0; cp != NULL; i++)
array[i] = strsep(&cp, ",");
array[i] = NULL;
/*
* Return the new array of pointers
*/
return array;
}
+1 -1
View File
@@ -81,5 +81,5 @@ int ulckpwdf (void)
return (pw_unlock () && spw_unlock ())? 0 : -1;
}
#else
extern int errno; /* warning: ANSI C forbids an empty source file */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif
+109
View File
@@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <sys/types.h>
#include <pwd.h>
#include <fcntl.h>
#include <time.h>
#include "defines.h"
#include <lastlog.h>
#include "memzero.h"
#include "prototypes.h"
#include "string/strncpy.h"
#include "string/strtcpy.h"
/*
* dolastlog - create lastlog entry
*
* A "last login" entry is created for the user being logged in. The
* UID is extracted from the global (struct passwd) entry and the
* TTY information is gotten from the (struct utmpx).
*/
void dolastlog (
struct lastlog *ll,
const struct passwd *pw,
/*@unique@*/const char *line,
/*@unique@*/const char *host)
{
int fd;
off_t offset;
struct lastlog newlog;
time_t ll_time;
/*
* If the file does not exist, don't create it.
*/
fd = open (LASTLOG_FILE, O_RDWR);
if (-1 == fd) {
return;
}
/*
* The file is indexed by UID number. Seek to the record
* for this UID. Negative UID's will create problems, but ...
*/
offset = (off_t) pw->pw_uid * sizeof newlog;
if (lseek (fd, offset, SEEK_SET) != offset) {
SYSLOG ((LOG_WARN,
"Can't read last lastlog entry for UID %lu in %s. Entry not updated.",
(unsigned long) pw->pw_uid, LASTLOG_FILE));
(void) close (fd);
return;
}
/*
* Read the old entry so we can tell the user when they last
* logged in. Then construct the new entry and write it out
* the way we read the old one in.
*/
if (read (fd, &newlog, sizeof newlog) != (ssize_t) sizeof newlog) {
memzero (&newlog, sizeof newlog);
}
if (NULL != ll) {
*ll = newlog;
}
ll_time = newlog.ll_time;
(void) time (&ll_time);
newlog.ll_time = ll_time;
STRTCPY(newlog.ll_line, line);
#if HAVE_LL_HOST
STRNCPY(newlog.ll_host, host);
#endif
if ( (lseek (fd, offset, SEEK_SET) != offset)
|| (write_full(fd, &newlog, sizeof newlog) == -1)) {
goto err_write;
}
if (close (fd) != 0 && errno != EINTR) {
goto err_close;
}
return;
err_write:
{
int saved_errno = errno;
(void) close (fd);
errno = saved_errno;
}
err_close:
SYSLOG ((LOG_WARN,
"Can't write lastlog entry for UID %lu in %s: %m",
(unsigned long) pw->pw_uid, LASTLOG_FILE));
}
+52
View File
@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2023, Iker Pedrosa <ipedrosa@redhat.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include "attr.h"
#include "defines.h"
#include "prototypes.h"
#include <systemd/sd-login.h>
int get_session_host (char **out)
{
char *host = NULL;
char *session = NULL;
int ret;
ret = sd_pid_get_session (getpid(), &session);
if (ret < 0) {
return ret;
}
ret = sd_session_get_remote_host (session, &host);
if (ret < 0) {
goto done;
}
*out = host;
done:
free (session);
return ret;
}
unsigned long active_sessions_count(const char *name, MAYBE_UNUSED unsigned long limit)
{
struct passwd *pw;
unsigned long count = 0;
pw = prefix_getpwnam(name);
if (pw == NULL) {
return 0;
}
count = sd_uid_get_sessions(pw->pw_uid, 0, NULL);
return count;
}
+111
View File
@@ -0,0 +1,111 @@
/*
* SPDX-FileCopyrightText: 1989 - 1993, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 - 2011, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <assert.h>
#include <stdio.h>
#include <signal.h>
#include "alloc.h"
#include "attr.h"
#include "memzero.h"
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
static void login_exit (MAYBE_UNUSED int sig)
{
_exit (EXIT_FAILURE);
}
/*
* login_prompt - prompt the user for their login name
*
* login_prompt() displays the standard login prompt. If ISSUE_FILE
* is set in login.defs, this file is displayed before the prompt.
*/
void login_prompt (char *name, int namesize)
{
char buf[1024];
char *cp;
int i;
FILE *fp;
const char *fname = getdef_str ("ISSUE_FILE");
sighandler_t sigquit;
sighandler_t sigtstp;
/*
* There is a small chance that a QUIT character will be part of
* some random noise during a prompt. Deal with this by exiting
* instead of core dumping. Do the same thing for SIGTSTP.
*/
sigquit = signal (SIGQUIT, login_exit);
sigtstp = signal (SIGTSTP, login_exit);
/*
* See if the user has configured the issue file to
* be displayed and display it before the prompt.
*/
if (NULL != fname) {
fp = fopen (fname, "r");
if (NULL != fp) {
while ((i = getc (fp)) != EOF) {
(void) putc (i, stdout);
}
(void) fclose (fp);
}
}
(void) gethostname (buf, sizeof buf);
printf (_("\n%s login: "), buf);
(void) fflush (stdout);
/*
* Read the user's response. The trailing newline will be
* removed.
*/
MEMZERO(buf);
if (fgets (buf, sizeof buf, stdin) != buf) {
exit (EXIT_FAILURE);
}
cp = strchr (buf, '\n');
if (NULL == cp) {
exit (EXIT_FAILURE);
}
*cp = '\0'; /* remove \n [ must be there ] */
/*
* Skip leading whitespace. This makes " username" work right.
* Then copy the rest (up to the end) into the username.
*/
for (cp = buf; *cp == ' ' || *cp == '\t'; cp++);
for (i = 0; i < namesize - 1 && *cp != '\0'; name[i++] = *cp++);
name[i] = '\0';
/*
* Set the SIGQUIT handler back to its original value
*/
(void) signal (SIGQUIT, sigquit);
(void) signal (SIGTSTP, sigtstp);
}
+67
View File
@@ -0,0 +1,67 @@
/*
* SPDX-FileCopyrightText: 1989 - 1991, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "prototypes.h"
#include "defines.h"
#include <assert.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include "alloc.h"
#include "getdef.h"
#include "string/sprintf.h"
#ident "$Id$"
void mailcheck (void)
{
struct stat statbuf;
char *mailbox;
if (!getdef_bool ("MAIL_CHECK_ENAB")) {
return;
}
/*
* Check incoming mail in Maildir format - J.
*/
mailbox = getenv ("MAILDIR");
if (NULL != mailbox) {
char *newmail;
xasprintf(&newmail, "%s/new", mailbox);
if (stat (newmail, &statbuf) != -1 && statbuf.st_size != 0) {
if (statbuf.st_mtime > statbuf.st_atime) {
free(newmail);
(void) puts (_("You have new mail."));
return;
}
}
free(newmail);
}
mailbox = getenv ("MAIL");
if (NULL == mailbox) {
return;
}
if ( (stat (mailbox, &statbuf) == -1)
|| (statbuf.st_size == 0)) {
(void) puts (_("No mail."));
} else if (statbuf.st_atime > statbuf.st_mtime) {
(void) puts (_("You have mail."));
} else {
(void) puts (_("You have new mail."));
}
}
+17
View File
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <stddef.h>
#include "memzero.h"
extern inline void memzero(void *ptr, size_t size);
extern inline void strzero(char *s);
+49
View File
@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2022-2023, Christian Göttsche <cgzones@googlemail.com>
* SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SHADOW_INCLUDE_LIBMISC_MEMZERO_H_
#define SHADOW_INCLUDE_LIBMISC_MEMZERO_H_
#include <config.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include "sizeof.h"
#define MEMZERO(arr) memzero(arr, SIZEOF_ARRAY(arr))
inline void memzero(void *ptr, size_t size);
inline void strzero(char *s);
inline void
memzero(void *ptr, size_t size)
{
#if defined(HAVE_MEMSET_EXPLICIT)
memset_explicit(ptr, 0, size);
#elif defined(HAVE_EXPLICIT_BZERO)
explicit_bzero(ptr, size);
#else
bzero(ptr, size);
__asm__ __volatile__ ("" : : "r"(ptr) : "memory");
#endif
}
inline void
strzero(char *s)
{
memzero(s, strlen(s));
}
#endif // include guard
+60
View File
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 1989 - 1991, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1997, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2010 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <stdio.h>
#include "alloc.h"
#include "defines.h"
#include "getdef.h"
#include "prototypes.h"
/*
* motd -- output the /etc/motd file
*
* motd() determines the name of a login announcement file and outputs
* it to the user's terminal at login time. The MOTD_FILE configuration
* option is a colon-delimited list of filenames.
*/
void motd (void)
{
FILE *fp;
char *motdlist;
const char *motdfile;
char *mb;
int c;
motdfile = getdef_str ("MOTD_FILE");
if (NULL == motdfile) {
return;
}
motdlist = xstrdup (motdfile);
for (mb = motdlist; ;mb = NULL) {
motdfile = strtok (mb, ":");
if (NULL == motdfile) {
break;
}
fp = fopen (motdfile, "r");
if (NULL != fp) {
while ((c = getc (fp)) != EOF) {
putchar (c);
}
fclose (fp);
}
}
fflush (stdout);
free (motdlist);
}
+99
View File
@@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2019-2023, Alejandro Colomar <alx@kernel.org>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SHADOW_INCLUDE_LIBMISC_MUST_BE_H_
#define SHADOW_INCLUDE_LIBMISC_MUST_BE_H_
#include <config.h>
#include <assert.h>
/*
* SYNOPSIS
* int must_be(bool e);
*
* ARGUMENTS
* e Expression to be asserted.
*
* DESCRIPTION
* This macro fails compilation if 'e' is false. If 'e' is true,
* it returns (int) 0, so it doesn't affect the expression in which
* it is contained.
*
* This macro is similar to static_assert(3). While
* static_assert(3) can only be used where a statement is allowed,
* this must_be() macro can be used wherever an expression is
* allowed.
*
* RETURN VALUE
* 0
*
* ERRORS
* If 'e' is false, the compilation will fail, as when using
* static_assert(3).
*
* EXAMPLES
* #define must_be_array(a) must_be(is_array(a))
*
* #define NITEMS(a) (sizeof(a) / sizeof(*(a)) + must_be_array(a))
*
* int foo[42];
* int bar[NITEMS(foo)];
*/
#define must_be(e) \
( \
0 * (int) sizeof( \
struct { \
static_assert(e, ""); \
int ISO_C_forbids_a_struct_with_no_members_; \
} \
) \
)
/*
* SYNOPSIS
* int must_be_array(a);
*
* ARGUMENTS
* a Array.
*
* DESCRIPTION
* This macro fails compilation if 'a' is not an array. It is
* useful in macros that accept an array as a parameter, where this
* macro can validate the macro argument. It prevent passing a
* pointer to such macros, which would otherwise produce silent
* bugs.
*
* RETURN VALUE
* 0
*
* ERRORS
* If 'a' is not an array, the compilation will fail.
*
* EXAMPLES
* int a[10];
* int *p;
*
* must_be_array(a); // Ok
* must_be_array(p); // Compile-time error
*
* SEE ALSO
* must_be()
*/
#define is_same_type(a, b) __builtin_types_compatible_p(a, b)
#define is_same_typeof(a, b) is_same_type(typeof(a), typeof(b))
#define is_array(a) (!is_same_typeof((a), &(a)[0]))
#define must_be_array(a) must_be(is_array(a))
#endif // include guard
+49
View File
@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 1996 - 1997, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* myname.c - determine the current username and get the passwd entry
*
*/
#include <config.h>
#ident "$Id$"
#include "defines.h"
#include <pwd.h>
#include "prototypes.h"
/*@null@*/ /*@only@*/struct passwd *get_my_pwent (void)
{
struct passwd *pw;
const char *cp = getlogin ();
uid_t ruid = getuid ();
/*
* Try getlogin() first - if it fails or returns a non-existent
* username, or a username which doesn't match the real UID, fall
* back to getpwuid(getuid()). This should work reasonably with
* usernames longer than the utmp limit (8 characters), as well as
* shared UIDs - but not both at the same time...
*
* XXX - when running from su, will return the current user (not
* the original user, like getlogin() does). Does this matter?
*/
if ((NULL != cp) && ('\0' != *cp)) {
pw = xgetpwnam (cp);
if ((NULL != pw) && (pw->pw_uid == ruid)) {
return pw;
}
if (NULL != pw) {
pw_free (pw);
}
}
return xgetpwuid (ruid);
}
+1 -1
View File
@@ -53,6 +53,6 @@ int nscd_flush_cache (const char *service)
return 0;
}
#else /* USE_NSCD */
extern int errno; /* warning: ANSI C forbids an empty source file */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* USE_NSCD */
+71 -67
View File
@@ -1,3 +1,5 @@
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
@@ -6,10 +8,14 @@
#include <strings.h>
#include <ctype.h>
#include <stdatomic.h>
#include "alloc.h"
#include "prototypes.h"
#include "../libsubid/subid.h"
#include "shadowlog_internal.h"
#include "shadowlog.h"
#include "string/sprintf.h"
#define NSSWITCH "/etc/nsswitch.conf"
@@ -40,10 +46,12 @@ static void nss_exit(void) {
// nsswitch_path is an argument only to support testing.
void nss_init(const char *nsswitch_path) {
FILE *nssfp = NULL;
char *line = NULL, *p, *token, *saveptr;
size_t len = 0;
FILE *shadow_logfd = log_get_logfd();
char *line = NULL, *p, *token, *saveptr;
char libname[64];
FILE *nssfp = NULL;
FILE *shadow_logfd = log_get_logfd();
void *h;
size_t len = 0;
if (atomic_flag_test_and_set(&nss_init_started)) {
// Another thread has started nss_init, wait for it to complete
@@ -59,82 +67,78 @@ void nss_init(const char *nsswitch_path) {
// subid: files
nssfp = fopen(nsswitch_path, "r");
if (!nssfp) {
if (errno != ENOENT)
fprintf(shadow_logfd, "Failed opening %s: %m\n", nsswitch_path);
atomic_store(&nss_init_completed, true);
return;
}
while ((getline(&line, &len, nssfp)) != -1) {
if (line[0] == '\0' || line[0] == '#')
p = NULL;
while (getline(&line, &len, nssfp) != -1) {
if (line[0] == '#')
continue;
if (strlen(line) < 8)
continue;
if (strncasecmp(line, "subid:", 6) != 0)
continue;
p = &line[6];
while ((*p) && isspace(*p))
while (isspace(*p))
p++;
if (!*p)
continue;
for (token = strtok_r(p, " \n\t", &saveptr);
token;
token = strtok_r(NULL, " \n\t", &saveptr)) {
char libname[65];
void *h;
if (strcmp(token, "files") == 0) {
subid_nss = NULL;
goto done;
}
if (strlen(token) > 50) {
fprintf(shadow_logfd, "Subid NSS module name too long (longer than 50 characters): %s\n", token);
fprintf(shadow_logfd, "Using files\n");
subid_nss = NULL;
goto done;
}
snprintf(libname, 64, "libsubid_%s.so", token);
h = dlopen(libname, RTLD_LAZY);
if (!h) {
fprintf(shadow_logfd, "Error opening %s: %s\n", libname, dlerror());
fprintf(shadow_logfd, "Using files\n");
subid_nss = NULL;
goto done;
}
subid_nss = malloc(sizeof(*subid_nss));
if (!subid_nss) {
dlclose(h);
goto done;
}
subid_nss->has_range = dlsym(h, "shadow_subid_has_range");
if (!subid_nss->has_range) {
fprintf(shadow_logfd, "%s did not provide @has_range@\n", libname);
dlclose(h);
free(subid_nss);
subid_nss = NULL;
goto done;
}
subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
if (!subid_nss->list_owner_ranges) {
fprintf(shadow_logfd, "%s did not provide @list_owner_ranges@\n", libname);
dlclose(h);
free(subid_nss);
subid_nss = NULL;
goto done;
}
subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
if (!subid_nss->find_subid_owners) {
fprintf(shadow_logfd, "%s did not provide @find_subid_owners@\n", libname);
dlclose(h);
free(subid_nss);
subid_nss = NULL;
goto done;
}
subid_nss->handle = h;
goto done;
}
if (*p != '\0')
break;
p = NULL;
}
if (p == NULL) {
goto null_subid;
}
token = strtok_r(p, " \n\t", &saveptr);
if (token == NULL) {
fprintf(shadow_logfd, "No usable subid NSS module found, using files\n");
// subid_nss has to be null here, but to ease reviews:
free(subid_nss);
subid_nss = NULL;
goto done;
goto null_subid;
}
if (strcmp(token, "files") == 0) {
goto null_subid;
}
if (strlen(token) > 50) {
fprintf(shadow_logfd, "Subid NSS module name too long (longer than 50 characters): %s\n", token);
fprintf(shadow_logfd, "Using files\n");
goto null_subid;
}
SNPRINTF(libname, "libsubid_%s.so", token);
h = dlopen(libname, RTLD_LAZY);
if (!h) {
fprintf(shadow_logfd, "Error opening %s: %s\n", libname, dlerror());
fprintf(shadow_logfd, "Using files\n");
goto null_subid;
}
subid_nss = MALLOC(1, struct subid_nss_ops);
if (!subid_nss) {
goto close_lib;
}
subid_nss->has_range = dlsym(h, "shadow_subid_has_range");
if (!subid_nss->has_range) {
fprintf(shadow_logfd, "%s did not provide @has_range@\n", libname);
goto close_lib;
}
subid_nss->list_owner_ranges = dlsym(h, "shadow_subid_list_owner_ranges");
if (!subid_nss->list_owner_ranges) {
fprintf(shadow_logfd, "%s did not provide @list_owner_ranges@\n", libname);
goto close_lib;
}
subid_nss->find_subid_owners = dlsym(h, "shadow_subid_find_subid_owners");
if (!subid_nss->find_subid_owners) {
fprintf(shadow_logfd, "%s did not provide @find_subid_owners@\n", libname);
goto close_lib;
}
subid_nss->handle = h;
goto done;
close_lib:
dlclose(h);
free(subid_nss);
null_subid:
subid_nss = NULL;
done:
atomic_store(&nss_init_completed, true);
+224
View File
@@ -0,0 +1,224 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <ctype.h>
#include <stdio.h>
#include "alloc.h"
#include "attr.h"
#include "memzero.h"
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
#if WITH_LIBBSD == 0
#include "freezero.h"
#endif /* WITH_LIBBSD */
/*
* can't be a palindrome - like `R A D A R' or `M A D A M'
*/
static bool palindrome (MAYBE_UNUSED const char *old, const char *new)
{
size_t i, j;
i = strlen (new);
for (j = 0; j < i; j++) {
if (new[i - j - 1] != new[j]) {
return false;
}
}
return true;
}
/*
* more than half of the characters are different ones.
*/
static bool similar (/*@notnull@*/const char *old, /*@notnull@*/const char *new)
{
int i, j;
/*
* XXX - sometimes this fails when changing from a simple password
* to a really long one (MD5). For now, I just return success if
* the new password is long enough. Please feel free to suggest
* something better... --marekm
*/
if (strlen (new) >= 8) {
return false;
}
for (i = j = 0; ('\0' != new[i]) && ('\0' != old[i]); i++) {
if (strchr (new, old[i]) != NULL) {
j++;
}
}
if (i >= j * 2) {
return false;
}
return true;
}
static char *str_lower (/*@returned@*/char *string)
{
char *cp;
for (cp = string; '\0' != *cp; cp++) {
*cp = tolower (*cp);
}
return string;
}
static /*@observer@*//*@null@*/const char *password_check (
/*@notnull@*/const char *old,
/*@notnull@*/const char *new,
/*@notnull@*/MAYBE_UNUSED const struct passwd *pwdp)
{
const char *msg = NULL;
char *oldmono, *newmono, *wrapped;
if (strcmp (new, old) == 0) {
return _("no change");
}
newmono = str_lower (xstrdup (new));
oldmono = str_lower (xstrdup (old));
wrapped = XMALLOC(strlen(oldmono) * 2 + 1, char);
strcpy (wrapped, oldmono);
strcat (wrapped, oldmono);
if (palindrome (oldmono, newmono)) {
msg = _("a palindrome");
} else if (strcmp (oldmono, newmono) == 0) {
msg = _("case changes only");
} else if (similar (oldmono, newmono)) {
msg = _("too similar");
} else if (strstr (wrapped, newmono) != NULL) {
msg = _("rotated");
}
strzero (newmono);
strzero (oldmono);
strzero (wrapped);
free (newmono);
free (oldmono);
free (wrapped);
return msg;
}
static /*@observer@*//*@null@*/const char *obscure_msg (
/*@notnull@*/const char *old,
/*@notnull@*/const char *new,
/*@notnull@*/const struct passwd *pwdp)
{
size_t maxlen, oldlen, newlen;
char *new1, *old1;
const char *msg;
const char *result;
oldlen = strlen (old);
newlen = strlen (new);
if (newlen < (size_t) getdef_num ("PASS_MIN_LEN", 0)) {
return _("too short");
}
/*
* Remaining checks are optional.
*/
if (!getdef_bool ("OBSCURE_CHECKS_ENAB")) {
return NULL;
}
msg = password_check (old, new, pwdp);
if (NULL != msg) {
return msg;
}
result = getdef_str ("ENCRYPT_METHOD");
if (NULL == result) {
/* The traditional crypt() truncates passwords to 8 chars. It is
possible to circumvent the above checks by choosing an easy
8-char password and adding some random characters to it...
Example: "password$%^&*123". So check it again, this time
truncated to the maximum length. Idea from npasswd. --marekm */
if (getdef_bool ("MD5_CRYPT_ENAB")) {
return NULL;
}
} else {
if ( (strcmp (result, "MD5") == 0)
#ifdef USE_SHA_CRYPT
|| (strcmp (result, "SHA256") == 0)
|| (strcmp (result, "SHA512") == 0)
#endif
#ifdef USE_BCRYPT
|| (strcmp (result, "BCRYPT") == 0)
#endif
#ifdef USE_YESCRYPT
|| (strcmp (result, "YESCRYPT") == 0)
#endif
) {
return NULL;
}
}
maxlen = getdef_num ("PASS_MAX_LEN", 8);
if ( (oldlen <= maxlen)
&& (newlen <= maxlen)) {
return NULL;
}
new1 = xstrdup (new);
old1 = xstrdup (old);
if (newlen > maxlen) {
new1[maxlen] = '\0';
}
if (oldlen > maxlen) {
old1[maxlen] = '\0';
}
msg = password_check (old1, new1, pwdp);
freezero (new1, newlen);
freezero (old1, oldlen);
return msg;
}
/*
* Obscure - see if password is obscure enough.
*
* The programmer is encouraged to add as much complexity to this
* routine as desired. Included are some of my favorite ways to
* check passwords.
*/
bool obscure (const char *old, const char *new, const struct passwd *pwdp)
{
const char *msg = obscure_msg (old, new, pwdp);
if (NULL != msg) {
printf (_("Bad password: %s. "), msg);
return false;
}
return true;
}
+1 -1
View File
@@ -15,7 +15,7 @@
#endif
static struct pam_conv conv = {
static const struct pam_conv conv = {
SHADOW_PAM_CONVERSATION,
NULL
};
+59
View File
@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 1997 - 1999, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2001 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ifdef USE_PAM
#ident "$Id$"
/*
* Change the user's password using PAM.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include "defines.h"
#include "pam_defs.h"
#include "prototypes.h"
#include "shadowlog.h"
void do_pam_passwd (const char *user, bool silent, bool change_expired)
{
pam_handle_t *pamh = NULL;
int flags = 0, ret;
FILE *shadow_logfd = log_get_logfd();
if (silent)
flags |= PAM_SILENT;
if (change_expired)
flags |= PAM_CHANGE_EXPIRED_AUTHTOK;
ret = pam_start ("passwd", user, &conv, &pamh);
if (ret != PAM_SUCCESS) {
fprintf (shadow_logfd,
_("passwd: pam_start() failed, error %d\n"), ret);
exit (10); /* XXX */
}
ret = pam_chauthtok (pamh, flags);
if (ret != PAM_SUCCESS) {
fprintf (shadow_logfd, _("passwd: %s\n"), pam_strerror (pamh, ret));
fputs (_("passwd: password unchanged\n"), shadow_logfd);
pam_end (pamh, ret);
exit (10); /* XXX */
}
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 */
+149
View File
@@ -0,0 +1,149 @@
/*
* SPDX-FileCopyrightText: 2009 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id:$"
#ifdef USE_PAM
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <security/pam_appl.h>
#include "alloc.h"
#include "attr.h"
#include "memzero.h"
#include "prototypes.h"
#include "shadowlog.h"
/*@null@*/ /*@only@*/static const char *non_interactive_password = NULL;
static int ni_conv (int num_msg,
const struct pam_message **msg,
struct pam_response **resp,
MAYBE_UNUSED void *appdata_ptr);
static const struct pam_conv non_interactive_pam_conv = {
ni_conv,
NULL
};
static int ni_conv (int num_msg,
const struct pam_message **msg,
struct pam_response **resp,
MAYBE_UNUSED void *appdata_ptr)
{
struct pam_response *responses;
int count;
assert (NULL != non_interactive_password);
if (num_msg <= 0) {
return PAM_CONV_ERR;
}
responses = CALLOC (num_msg, struct pam_response);
if (NULL == responses) {
return PAM_CONV_ERR;
}
for (count=0; count < num_msg; count++) {
responses[count].resp_retcode = 0;
switch (msg[count]->msg_style) {
case PAM_PROMPT_ECHO_ON:
fprintf (log_get_logfd(),
_("%s: PAM modules requesting echoing are not supported.\n"),
log_get_progname());
goto failed_conversation;
case PAM_PROMPT_ECHO_OFF:
responses[count].resp = strdup (non_interactive_password);
if (NULL == responses[count].resp) {
goto failed_conversation;
}
break;
case PAM_ERROR_MSG:
if ( (NULL == msg[count]->msg)
|| (fprintf (log_get_logfd(), "%s\n", msg[count]->msg) <0)) {
goto failed_conversation;
}
responses[count].resp = NULL;
break;
case PAM_TEXT_INFO:
if ( (NULL == msg[count]->msg)
|| (fprintf (stdout, "%s\n", msg[count]->msg) <0)) {
goto failed_conversation;
}
responses[count].resp = NULL;
break;
default:
(void) fprintf (log_get_logfd(),
_("%s: conversation type %d not supported.\n"),
log_get_progname(), msg[count]->msg_style);
goto failed_conversation;
}
}
*resp = responses;
return PAM_SUCCESS;
failed_conversation:
for (count=0; count < num_msg; count++) {
if (NULL != responses[count].resp) {
strzero(responses[count].resp);
free(responses[count].resp);
responses[count].resp = NULL;
}
}
free (responses);
*resp = NULL;
return PAM_CONV_ERR;
}
/*
* Change non interactively the user's password using PAM.
*
* Return 0 on success, 1 on failure.
*/
int do_pam_passwd_non_interactive (const char *pam_service,
const char *username,
const char* password)
{
pam_handle_t *pamh = NULL;
int ret;
ret = pam_start (pam_service, username, &non_interactive_pam_conv, &pamh);
if (ret != PAM_SUCCESS) {
fprintf (log_get_logfd(),
_("%s: (user %s) pam_start failure %d\n"),
log_get_progname(), username, ret);
return 1;
}
non_interactive_password = password;
ret = pam_chauthtok (pamh, 0);
if (ret != PAM_SUCCESS) {
fprintf (log_get_logfd(),
_("%s: (user %s) pam_chauthtok() failed, error:\n"
"%s\n"),
log_get_progname(), username, pam_strerror (pamh, ret));
}
(void) pam_end (pamh, PAM_SUCCESS);
return ((PAM_SUCCESS == ret) ? 0 : 1);
}
#else /* !USE_PAM */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* !USE_PAM */
+13 -21
View File
@@ -79,7 +79,7 @@ static void endportent (void)
(void) fclose (ports);
}
ports = (FILE *) 0;
ports = NULL;
}
/*
@@ -127,14 +127,14 @@ static struct port *getportent (void)
* - parse off a list of days and times
*/
again:
again:
/*
* Get the next line and remove the last character, which
* is a '\n'. Lines which begin with '#' are all ignored.
* Get the next line and remove optional trailing '\n'.
* Lines which begin with '#' are all ignored.
*/
if (fgets (buf, (int) sizeof buf, ports) == 0) {
if (fgets (buf, sizeof buf, ports) == 0) {
errno = saveerr;
return 0;
}
@@ -149,18 +149,14 @@ static struct port *getportent (void)
* TTY devices.
*/
buf[strlen (buf) - 1] = 0;
buf[strcspn (buf, "\n")] = 0;
port.pt_names = ttys;
for (cp = buf, j = 0; j < PORT_TTY; j++) {
port.pt_names[j] = cp;
while (('\0' != *cp) && (':' != *cp) && (',' != *cp)) {
cp++;
}
if ('\0' == *cp) {
cp = strpbrk(cp, ":,");
if (cp == NULL)
goto again; /* line format error */
}
if (':' == *cp) { /* end of tty name list */
break;
@@ -172,13 +168,13 @@ static struct port *getportent (void)
}
*cp = '\0';
cp++;
port.pt_names[j + 1] = (char *) 0;
port.pt_names[j + 1] = NULL;
/*
* Get the list of user names. It is the second colon
* separated field, and is a comma separated list of user
* names. The entry '*' is used to specify all usernames.
* The last entry in the list is a (char *) 0 pointer.
* The last entry in the list is a NULL pointer.
*/
if (':' != *cp) {
@@ -243,9 +239,7 @@ static struct port *getportent (void)
* week or the other two values.
*/
for (i = 0;
('\0' != cp[i]) && ('\0' != cp[i + 1]) && isalpha (cp[i]);
i += 2) {
for (i = 0; isalpha(cp[i]) && ('\0' != cp[i + 1]); i += 2) {
switch ((cp[i] << 8) | (cp[i + 1])) {
case ('S' << 8) | 'u':
port.pt_times[j].t_days |= 01;
@@ -294,7 +288,7 @@ static struct port *getportent (void)
* representing the times of day.
*/
for (dtime = 0; ('\0' != cp[i]) && isdigit (cp[i]); i++) {
for (dtime = 0; isdigit (cp[i]); i++) {
dtime = dtime * 10 + cp[i] - '0';
}
@@ -304,9 +298,7 @@ static struct port *getportent (void)
port.pt_times[j].t_start = dtime;
cp = cp + i + 1;
for (dtime = 0, i = 0;
('\0' != cp[i]) && isdigit (cp[i]);
i++) {
for (dtime = 0, i = 0; isdigit (cp[i]); i++) {
dtime = dtime * 10 + cp[i] - '0';
}
+360
View File
@@ -0,0 +1,360 @@
/*
* SPDX-FileCopyrightText: 2011 , Julian Pidancet
* SPDX-FileCopyrightText: 2011 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <stdio.h>
#include <assert.h>
#include "defines.h"
#include "alloc.h"
#include "prototypes.h"
/*@-exitarg@*/
#include "exitcodes.h"
#include "groupio.h"
#include "pwio.h"
#ifdef SHADOWGRP
#include "sgroupio.h"
#endif
#include "shadowio.h"
#ifdef ENABLE_SUBIDS
#include "subordinateio.h"
#endif /* ENABLE_SUBIDS */
#include "getdef.h"
#include "shadowlog.h"
#include "string/sprintf.h"
static char *passwd_db_file = NULL;
static char *spw_db_file = NULL;
static char *group_db_file = NULL;
static char *sgroup_db_file = NULL;
static char *suid_db_file = NULL;
static char *sgid_db_file = NULL;
static char *def_conf_file = NULL;
static FILE* fp_pwent = NULL;
static FILE* fp_grent = NULL;
/*
* process_prefix_flag - prefix all paths if given the --prefix option
*
* This shall be called before accessing the passwd, group, shadow,
* gshadow, useradd's default, login.defs files (non exhaustive list)
* or authenticating the caller.
*
* The audit, syslog, or locale files shall be open before
*/
extern const char* process_prefix_flag (const char* short_opt, int argc, char **argv)
{
/*
* Parse the command line options.
*/
int i;
const char *prefix = NULL, *val;
for (i = 0; i < argc; i++) {
val = NULL;
if ( (strcmp (argv[i], "--prefix") == 0)
|| ((strncmp (argv[i], "--prefix=", 9) == 0)
&& (val = argv[i] + 9))
|| (strcmp (argv[i], short_opt) == 0)) {
if (NULL != prefix) {
fprintf (log_get_logfd(),
_("%s: multiple --prefix options\n"),
log_get_progname());
exit (E_BAD_ARG);
}
if (val) {
prefix = val;
} else if (i + 1 == argc) {
fprintf (log_get_logfd(),
_("%s: option '%s' requires an argument\n"),
log_get_progname(), argv[i]);
exit (E_BAD_ARG);
} else {
prefix = argv[++ i];
}
}
}
if (prefix != NULL) {
/* Drop privileges */
if ( (setregid (getgid (), getgid ()) != 0)
|| (setreuid (getuid (), getuid ()) != 0)) {
fprintf (log_get_logfd(),
_("%s: failed to drop privileges (%s)\n"),
log_get_progname(), strerror (errno));
exit (EXIT_FAILURE);
}
if ( prefix[0] == '\0' || !strcmp(prefix, "/"))
return ""; /* if prefix is "/" then we ignore the flag option */
/* should we prevent symbolic link from being used as a prefix? */
if ( prefix[0] != '/') {
fprintf (log_get_logfd(),
_("%s: prefix must be an absolute path\n"),
log_get_progname());
exit (E_BAD_ARG);
}
xasprintf(&passwd_db_file, "%s/%s", prefix, PASSWD_FILE);
pw_setdbname(passwd_db_file);
xasprintf(&group_db_file, "%s/%s", prefix, GROUP_FILE);
gr_setdbname(group_db_file);
#ifdef SHADOWGRP
xasprintf(&sgroup_db_file, "%s/%s", prefix, SGROUP_FILE);
sgr_setdbname(sgroup_db_file);
#endif
xasprintf(&spw_db_file, "%s/%s", prefix, SHADOW_FILE);
spw_setdbname(spw_db_file);
#ifdef ENABLE_SUBIDS
xasprintf(&suid_db_file, "%s/%s", prefix, SUBUID_FILE);
sub_uid_setdbname(suid_db_file);
xasprintf(&sgid_db_file, "%s/%s", prefix, SUBGID_FILE);
sub_gid_setdbname(sgid_db_file);
#endif
#ifdef USE_ECONF
setdef_config_file(prefix);
#else
xasprintf(&def_conf_file, "%s/%s", prefix, "/etc/login.defs");
setdef_config_file(def_conf_file);
#endif
}
if (prefix == NULL)
return "";
return prefix;
}
extern struct group *prefix_getgrnam(const char *name)
{
if (group_db_file) {
FILE* fg;
struct group * grp = NULL;
fg = fopen(group_db_file, "rt");
if (!fg)
return NULL;
while ((grp = fgetgrent(fg)) != NULL) {
if (!strcmp(name, grp->gr_name))
break;
}
fclose(fg);
return grp;
}
return getgrnam(name);
}
extern struct group *prefix_getgrgid(gid_t gid)
{
if (group_db_file) {
FILE* fg;
struct group * grp = NULL;
fg = fopen(group_db_file, "rt");
if (!fg)
return NULL;
while ((grp = fgetgrent(fg)) != NULL) {
if (gid == grp->gr_gid)
break;
}
fclose(fg);
return grp;
}
return getgrgid(gid);
}
extern struct passwd *prefix_getpwuid(uid_t uid)
{
if (passwd_db_file) {
FILE* fg;
struct passwd *pwd = NULL;
fg = fopen(passwd_db_file, "rt");
if (!fg)
return NULL;
while ((pwd = fgetpwent(fg)) != NULL) {
if (uid == pwd->pw_uid)
break;
}
fclose(fg);
return pwd;
}
else {
return getpwuid(uid);
}
}
extern struct passwd *prefix_getpwnam(const char* name)
{
if (passwd_db_file) {
FILE* fg;
struct passwd *pwd = NULL;
fg = fopen(passwd_db_file, "rt");
if (!fg)
return NULL;
while ((pwd = fgetpwent(fg)) != NULL) {
if (!strcmp(name, pwd->pw_name))
break;
}
fclose(fg);
return pwd;
}
else {
return getpwnam(name);
}
}
#if HAVE_FGETPWENT_R
extern int prefix_getpwnam_r(const char* name, struct passwd* pwd,
char* buf, size_t buflen, struct passwd** result)
{
if (passwd_db_file) {
FILE* fg;
int ret = 0;
fg = fopen(passwd_db_file, "rt");
if (!fg)
return errno;
while ((ret = fgetpwent_r(fg, pwd, buf, buflen, result)) == 0) {
if (!strcmp(name, pwd->pw_name))
break;
}
fclose(fg);
return ret;
}
else {
return getpwnam_r(name, pwd, buf, buflen, result);
}
}
#endif
extern struct spwd *prefix_getspnam(const char* name)
{
if (spw_db_file) {
FILE* fg;
struct spwd *sp = NULL;
fg = fopen(spw_db_file, "rt");
if (!fg)
return NULL;
while ((sp = fgetspent(fg)) != NULL) {
if (!strcmp(name, sp->sp_namp))
break;
}
fclose(fg);
return sp;
}
else {
return getspnam(name);
}
}
extern void prefix_setpwent(void)
{
if (!passwd_db_file) {
setpwent();
return;
}
if (fp_pwent)
fclose (fp_pwent);
fp_pwent = fopen(passwd_db_file, "rt");
if (!fp_pwent)
return;
}
extern struct passwd* prefix_getpwent(void)
{
if (!passwd_db_file) {
return getpwent();
}
if (!fp_pwent) {
return NULL;
}
return fgetpwent(fp_pwent);
}
extern void prefix_endpwent(void)
{
if (!passwd_db_file) {
endpwent();
return;
}
if (fp_pwent)
fclose(fp_pwent);
fp_pwent = NULL;
}
extern void prefix_setgrent(void)
{
if (!group_db_file) {
setgrent();
return;
}
if (fp_grent)
fclose (fp_grent);
fp_grent = fopen(group_db_file, "rt");
if (!fp_grent)
return;
}
extern struct group* prefix_getgrent(void)
{
if (!group_db_file) {
return getgrent();
}
return fgetgrent(fp_grent);
}
extern void prefix_endgrent(void)
{
if (!group_db_file) {
endgrent();
return;
}
if (fp_grent)
fclose(fp_grent);
fp_grent = NULL;
}
extern struct group *prefix_getgr_nam_gid(const char *grname)
{
long long gid;
char *endptr;
struct group *g;
if (NULL == grname) {
return NULL;
}
if (!group_db_file)
return getgr_nam_gid(grname);
errno = 0;
gid = strtoll(grname, &endptr, 10);
if ( ('\0' != *grname)
&& ('\0' == *endptr)
&& (0 == errno)
&& (gid == (gid_t)gid))
{
return prefix_getgrgid(gid);
}
g = prefix_getgrnam(grname);
return g ? __gr_dup(g) : NULL;
}
+100 -56
View File
@@ -10,7 +10,7 @@
/*
* prototypes.h
*
* prototypes of libmisc functions, and private lib functions.
* prototypes of some lib functions, and private lib functions.
*
* $Id$
*
@@ -21,18 +21,17 @@
#include <config.h>
#include <sys/socket.h>
#include <sys/stat.h>
#ifdef USE_UTMPX
#include <utmpx.h>
#else
#include <utmp.h>
#endif
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <shadow.h>
#ifdef ENABLE_LASTLOG
#include <lastlog.h>
#endif /* ENABLE_LASTLOG */
#include "attr.h"
#include "defines.h"
#include "commonio.h"
@@ -44,6 +43,7 @@ extern int add_groups (const char *);
/* age.c */
extern void agecheck (/*@null@*/const struct spwd *);
extern int expire (const struct passwd *, /*@null@*/const struct spwd *);
/* isexpired.c */
extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *);
@@ -92,11 +92,11 @@ void cleanup_report_del_group_gshadow (void *group_name);
void cleanup_report_mod_passwd (void *cleanup_info);
void cleanup_report_mod_group (void *cleanup_info);
void cleanup_report_mod_gshadow (void *cleanup_info);
void cleanup_unlock_group (/*@null@*/void *unused);
void cleanup_unlock_group (/*@null@*/void *MAYBE_UNUSED);
#ifdef SHADOWGRP
void cleanup_unlock_gshadow (/*@null@*/void *unused);
void cleanup_unlock_gshadow (/*@null@*/void *MAYBE_UNUSED);
#endif
void cleanup_unlock_passwd (/*@null@*/void *unused);
void cleanup_unlock_passwd (/*@null@*/void *MAYBE_UNUSED);
/* console.c */
extern bool console (const char *);
@@ -108,21 +108,18 @@ extern int copy_tree (const char *src_root, const char *dst_root,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
/* date_to_str.c */
extern void date_to_str (size_t size, char buf[size], long date);
/* encrypt.c */
extern /*@exposed@*//*@null@*/char *pw_encrypt (const char *, const char *);
/* entry.c */
extern void pw_entry (const char *, struct passwd *);
/* env.c */
extern void addenv (const char *, /*@null@*/const char *);
extern void initenv (void);
extern void set_env (int, char *const *);
extern void sanitize_env (void);
/* fd.c */
extern void check_fds (void);
/* fields.c */
extern void change_field (char *, size_t, const char *);
extern int valid_field (const char *, const char *);
@@ -153,10 +150,13 @@ extern int get_gid (const char *gidstr, gid_t *gid);
extern /*@only@*//*@null@*/struct group *getgr_nam_gid (/*@null@*/const char *grname);
/* getlong.c */
extern int getlong (const char *numstr, /*@out@*/long int *result);
ATTR_ACCESS(write_only, 2)
extern int getlong(const char *restrict numstr, long *restrict result);
/* get_pid.c */
extern int get_pid (const char *pidstr, pid_t *pid);
extern int get_pidfd_from_fd(const char *pidfdstr);
extern int open_pidfd(const char *pidstr);
/* getrange */
extern int getrange (const char *range,
@@ -170,10 +170,12 @@ extern time_t gettime (void);
extern int get_uid (const char *uidstr, uid_t *uid);
/* getulong.c */
extern int getulong (const char *numstr, /*@out@*/unsigned long int *result);
ATTR_ACCESS(write_only, 2)
extern int getulong(const char *restrict numstr, unsigned long *restrict result);
/* fputsx.c */
extern /*@null@*/char *fgetsx (/*@returned@*/ /*@out@*/char *, int, FILE *);
ATTR_ACCESS(write_only, 1, 2)
extern /*@null@*/char *fgetsx(/*@returned@*/char *restrict, int, FILE *restrict);
extern int fputsx (const char *, FILE *);
/* groupio.c */
@@ -185,8 +187,7 @@ extern void __gr_set_changed (void);
/* groupmem.c */
extern /*@null@*/ /*@only@*/struct group *__gr_dup (const struct group *grent);
extern void gr_free_members (struct group *grent);
extern void gr_free (/*@out@*/ /*@only@*/struct group *grent);
extern bool gr_append_member (struct group *grp, char *member);
extern void gr_free(/*@only@*/struct group *grent);
/* hushed.c */
extern bool hushed (const char *username);
@@ -212,24 +213,26 @@ extern void setup_limits (const struct passwd *);
#endif
/* list.c */
extern /*@only@*/ /*@out@*/char **add_list (/*@returned@*/ /*@only@*/char **, const char *);
extern /*@only@*/ /*@out@*/char **del_list (/*@returned@*/ /*@only@*/char **, const char *);
extern /*@only@*/ /*@out@*/char **dup_list (char *const *);
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 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 (
struct lastlog *ll,
const struct passwd *pw,
/*@unique@*/const char *line,
/*@unique@*/const char *host);
#endif /* ENABLE_LASTLOG */
/* login_nopam.c */
extern int login_access (const char *user, const char *from);
/* loginprompt.c */
extern void login_prompt (const char *, char *, int);
extern void login_prompt (char *, int);
/* mail.c */
extern void mailcheck (void);
@@ -304,9 +307,7 @@ extern int do_pam_passwd_non_interactive (const char *pam_service,
#endif /* USE_PAM */
/* obscure.c */
#ifndef USE_PAM
extern bool obscure (const char *, const char *, const struct passwd *);
#endif
/* pam_pass.c */
#ifdef USE_PAM
@@ -322,6 +323,10 @@ extern struct group *prefix_getgrnam(const char *name);
extern struct group *prefix_getgrgid(gid_t gid);
extern struct passwd *prefix_getpwuid(uid_t uid);
extern struct passwd *prefix_getpwnam(const char* name);
#if HAVE_FGETPWENT_R
extern int prefix_getpwnam_r(const char* name, struct passwd* pwd,
char* buf, size_t buflen, struct passwd** result);
#endif
extern struct spwd *prefix_getspnam(const char* name);
extern struct group *prefix_getgr_nam_gid(const char *grname);
extern void prefix_setpwent(void);
@@ -332,9 +337,7 @@ extern struct group* prefix_getgrent(void);
extern void prefix_endgrent(void);
/* pwd2spwd.c */
#ifndef USE_PAM
extern struct spwd *pwd_to_spwd (const struct passwd *);
#endif
/* pwdcheck.c */
#ifndef USE_PAM
@@ -351,14 +354,19 @@ extern /*@dependent@*/ /*@null@*/struct commonio_entry *__pw_get_head (void);
/* pwmem.c */
extern /*@null@*/ /*@only@*/struct passwd *__pw_dup (const struct passwd *pwent);
extern void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent);
extern void pw_free(/*@only@*/struct passwd *pwent);
/* csrand.c */
unsigned long csrand (void);
unsigned long csrand_uniform (unsigned long n);
unsigned long csrand_interval (unsigned long min, unsigned long max);
/* remove_tree.c */
extern int remove_tree (const char *root, bool remove_root);
/* rlogin.c */
extern int do_rlogin (const char *remote_host, char *name, size_t namelen,
char *term, size_t termlen);
extern int do_rlogin(const char *remote_host, char *name, size_t namesize,
char *term, size_t termsize);
/* root_flag.c */
extern void process_root_flag (const char* short_opt, int argc, char **argv);
@@ -376,7 +384,7 @@ extern int check_selinux_permit (const char *perm_name);
/* semanage.c */
#ifdef WITH_SELINUX
extern int set_seuser(const char *login_name, const char *seuser_name);
extern int set_seuser(const char *login_name, const char *seuser_name, const char *serange);
extern int del_seuser(const char *login_name);
#endif
@@ -409,7 +417,7 @@ extern struct spwd *sgetspent (const char *string);
/* sgroupio.c */
extern void __sgr_del_entry (const struct commonio_entry *ent);
extern /*@null@*/ /*@only@*/struct sgrp *__sgr_dup (const struct sgrp *sgent);
extern void sgr_free (/*@out@*/ /*@only@*/struct sgrp *sgent);
extern void sgr_free(/*@only@*/struct sgrp *sgent);
extern /*@dependent@*/ /*@null@*/struct commonio_entry *__sgr_get_head (void);
extern void __sgr_set_changed (void);
@@ -419,14 +427,15 @@ extern void __spw_del_entry (const struct commonio_entry *ent);
/* shadowmem.c */
extern /*@null@*/ /*@only@*/struct spwd *__spw_dup (const struct spwd *spent);
extern void spw_free (/*@out@*/ /*@only@*/struct spwd *spent);
extern void spw_free(/*@only@*/struct spwd *spent);
/* shell.c */
extern int shell (const char *file, /*@null@*/const char *arg, char *const envp[]);
/* spawn.c */
extern int run_command (const char *cmd, const char *argv[],
/*@null@*/const char *envp[], /*@out@*/int *status);
ATTR_ACCESS(write_only, 4)
extern int run_command(const char *cmd, const char *argv[],
/*@null@*/const char *envp[], int *restrict status);
/* strtoday.c */
extern long strtoday (const char *);
@@ -459,33 +468,68 @@ extern int set_filesize_limit (int blocks);
/* user_busy.c */
extern int user_busy (const char *name, uid_t uid);
/* utmp.c */
#ifndef USE_UTMPX
extern /*@null@*/struct utmp *get_current_utmp (void);
extern struct utmp *prepare_utmp (const char *name,
const char *line,
const char *host,
/*@null@*/const struct utmp *ut);
extern int setutmp (struct utmp *ut);
#else
extern /*@null@*/struct utmpx *get_current_utmp (void);
extern struct utmpx *prepare_utmpx (const char *name,
const char *line,
const char *host,
/*@null@*/const struct utmpx *ut);
extern int setutmpx (struct utmpx *utx);
#endif /* USE_UTMPX */
/*
* Session management: utmp.c or logind.c
*/
/**
* @brief Get host for the current session
*
* @param[out] out Host name
*
* @return 0 or a positive integer if the host was obtained properly,
* another value on error.
*/
extern int get_session_host (char **out);
#ifndef ENABLE_LOGIND
/**
* @brief Update or create an utmp entry in utmp, wtmp, utmpw, or wtmpx
*
* @param[in] user username
* @param[in] tty tty
* @param[in] host hostname
*
* @return 0 if utmp was updated properly,
* 1 on error.
*/
extern int update_utmp (const char *user,
const char *tty,
const char *host);
/**
* @brief Update the cumulative failure log
*
* @param[in] failent_user username
* @param[in] tty tty
* @param[in] host hostname
*
*/
extern void record_failure(const char *failent_user,
const char *tty,
const char *hostname);
#endif /* ENABLE_LOGIND */
/**
* @brief Number of active user sessions
*
* @param[in] name username
* @param[in] limit maximum number of active sessions
*
* @return number of active sessions.
*
*/
extern unsigned long active_sessions_count(const char *name,
unsigned long limit);
/* valid.c */
extern bool valid (const char *, const struct passwd *);
/* xmalloc.c */
extern /*@maynotreturn@*/ /*@only@*//*@out@*//*@notnull@*/void *xmalloc (size_t size)
/*@ensures MaxSet(result) == (size - 1); @*/;
extern /*@maynotreturn@*/ /*@only@*//*@notnull@*/char *xstrdup (const char *);
/* write_full.c */
extern int write_full(int fd, const void *buf, size_t count);
/* xgetpwnam.c */
extern /*@null@*/ /*@only@*/struct passwd *xgetpwnam (const char *);
/* xprefix_getpwnam.c */
extern /*@null@*/ /*@only@*/struct passwd *xprefix_getpwnam (const char *);
/* xgetpwuid.c */
extern /*@null@*/ /*@only@*/struct passwd *xgetpwuid (uid_t);
/* xgetgrnam.c */
+24 -40
View File
@@ -18,21 +18,26 @@
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include "prototypes.h"
#include "agetpass.h"
#include "defines.h"
#include "memzero.h"
#include "prototypes.h"
#include "pwauth.h"
#include "getdef.h"
#include "string/sprintf.h"
#ifdef SKEY
#include <skey.h>
#endif
#ifdef __linux__ /* standard password prompt by default */
static const char *PROMPT = gettext_noop ("Password: ");
#else
static const char *PROMPT = gettext_noop ("%s's Password: ");
#endif
bool wipe_clear_pass = true;
/*@null@*/char *clear_pass = NULL;
/*
* pw_auth - perform getpass/crypt authentication
@@ -47,16 +52,16 @@ int pw_auth (const char *cipher,
int reason,
/*@null@*/const char *input)
{
char prompt[1024];
char *clear = NULL;
const char *cp;
const char *encrypted;
int retval;
int retval;
char prompt[1024];
char *clear = NULL;
const char *cp;
const char *encrypted;
#ifdef SKEY
bool use_skey = false;
char challenge_info[40];
struct skey skey;
bool use_skey = false;
char challenge_info[40];
struct skey skey;
#endif
/*
@@ -137,15 +142,9 @@ int pw_auth (const char *cipher,
}
#endif
snprintf (prompt, sizeof prompt, cp, user);
clear = getpass (prompt);
if (NULL == clear) {
static char c[1];
c[0] = '\0';
clear = c;
}
input = clear;
SNPRINTF(prompt, cp, user);
clear = agetpass(prompt);
input = (clear == NULL) ? "" : clear;
}
/*
@@ -171,14 +170,9 @@ int pw_auth (const char *cipher,
* -- AR 8/22/1999
*/
if ((0 != retval) && ('\0' == input[0]) && use_skey) {
clear = getpass (prompt);
if (NULL == clear) {
static char c[1];
c[0] = '\0';
clear = c;
}
input = clear;
erase_pass(clear);
clear = agetpass(prompt);
input = (clear == NULL) ? "" : clear;
}
if ((0 != retval) && use_skey) {
@@ -192,20 +186,10 @@ int pw_auth (const char *cipher,
}
}
#endif
erase_pass(clear);
/*
* Things like RADIUS authentication may need the password -
* if the external variable wipe_clear_pass is zero, we will
* not wipe it (the caller should wipe clear_pass when it is
* no longer needed). --marekm
*/
clear_pass = clear;
if (wipe_clear_pass && (NULL != clear) && ('\0' != *clear)) {
strzero (clear);
}
return retval;
}
#else /* !USE_PAM */
extern int errno; /* warning: ANSI C forbids an empty source file */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* !USE_PAM */
+62
View File
@@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1997, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2009, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <sys/types.h>
#include "prototypes.h"
#include "defines.h"
#include <pwd.h>
/*
* pwd_to_spwd - create entries for new spwd structure
*
* pwd_to_spwd() creates a new (struct spwd) containing the
* information in the pointed-to (struct passwd).
*/
struct spwd *pwd_to_spwd (const struct passwd *pw)
{
static struct spwd sp;
/*
* Nice, easy parts first. The name and passwd map directly
* from the old password structure to the new one.
*/
sp.sp_namp = pw->pw_name;
sp.sp_pwdp = pw->pw_passwd;
{
/*
* Defaults used if there is no pw_age information.
*/
sp.sp_min = 0;
sp.sp_max = 10000;
sp.sp_lstchg = gettime () / DAY;
if (0 == sp.sp_lstchg) {
/* Better disable aging than requiring a password
* change */
sp.sp_lstchg = -1;
}
}
/*
* These fields have no corresponding information in the password
* file. They are set to uninitialized values.
*/
sp.sp_warn = -1;
sp.sp_expire = -1;
sp.sp_inact = -1;
sp.sp_flag = SHADOW_SP_FLAG_UNSET;
return &sp;
}
+56
View File
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1997 , Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include "defines.h"
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include "prototypes.h"
/*
* pwd_init - ignore signals, and set resource limits to safe
* values. Call this before modifying password files, so that
* it is less likely to fail in the middle of operation.
*/
void pwd_init (void)
{
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = 0;
setrlimit (RLIMIT_CORE, &rlim);
rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
setrlimit (RLIMIT_AS, &rlim);
setrlimit (RLIMIT_CPU, &rlim);
setrlimit (RLIMIT_DATA, &rlim);
setrlimit (RLIMIT_FSIZE, &rlim);
setrlimit (RLIMIT_NOFILE, &rlim);
#ifdef RLIMIT_RSS
setrlimit (RLIMIT_RSS, &rlim);
#endif
setrlimit (RLIMIT_STACK, &rlim);
signal (SIGALRM, SIG_IGN);
signal (SIGHUP, SIG_IGN);
signal (SIGINT, SIG_IGN);
signal (SIGPIPE, SIG_IGN);
signal (SIGQUIT, SIG_IGN);
signal (SIGTERM, SIG_IGN);
signal (SIGTSTP, SIG_IGN);
signal (SIGTTOU, SIG_IGN);
umask (077);
}
+40
View File
@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2000 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2008, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#ifndef USE_PAM
#include <stdio.h>
#include <shadow.h>
#include "attr.h"
#include "prototypes.h"
#include "defines.h"
#include "pwauth.h"
#include "shadowlog.h"
void passwd_check (const char *user, const char *passwd, MAYBE_UNUSED const char *progname)
{
struct spwd *sp;
sp = getspnam (user); /* !USE_PAM, no need for xgetspnam */
if (NULL != sp) {
passwd = sp->sp_pwdp;
}
if (pw_auth (passwd, user, PW_LOGIN, NULL) != 0) {
SYSLOG ((LOG_WARN, "incorrect password for `%s'", user));
(void) sleep (1);
fprintf (log_get_logfd(), _("Incorrect password for %s.\n"), user);
exit (EXIT_FAILURE);
}
}
#else /* USE_PAM */
extern int ISO_C_forbids_an_empty_translation_unit;
#endif /* USE_PAM */
+16 -7
View File
@@ -26,7 +26,8 @@ static /*@null@*/ /*@only@*/void *passwd_dup (const void *ent)
return __pw_dup (pw);
}
static void passwd_free (/*@out@*/ /*@only@*/void *ent)
static void
passwd_free(/*@only@*/void *ent)
{
struct passwd *pw = ent;
@@ -42,7 +43,7 @@ static const char *passwd_getname (const void *ent)
static void *passwd_parse (const char *line)
{
return (void *) sgetpwent (line);
return sgetpwent (line);
}
static int passwd_put (const void *ent, FILE * file)
@@ -137,7 +138,7 @@ int pw_open (int mode)
int pw_update (const struct passwd *pw)
{
return commonio_update (&passwd_db, (const void *) pw);
return commonio_update (&passwd_db, pw);
}
int pw_remove (const char *name)
@@ -182,15 +183,23 @@ struct commonio_db *__pw_get_db (void)
static int pw_cmp (const void *p1, const void *p2)
{
const struct commonio_entry *const *ce1;
const struct commonio_entry *const *ce2;
const struct passwd *pw1, *pw2;
uid_t u1, u2;
if ((*(struct commonio_entry **) p1)->eptr == NULL)
ce1 = p1;
pw1 = (*ce1)->eptr;
if (pw1 == NULL)
return 1;
if ((*(struct commonio_entry **) p2)->eptr == NULL)
ce2 = p2;
pw2 = (*ce2)->eptr;
if (pw2 == NULL)
return -1;
u1 = ((struct passwd *) (*(struct commonio_entry **) p1)->eptr)->pw_uid;
u2 = ((struct passwd *) (*(struct commonio_entry **) p2)->eptr)->pw_uid;
u1 = pw1->pw_uid;
u2 = pw2->pw_uid;
if (u1 < u2)
return -1;
+6 -3
View File
@@ -13,7 +13,10 @@
#ident "$Id$"
#include <stdio.h>
#include "alloc.h"
#include "defines.h"
#include "memzero.h"
#include "prototypes.h"
#include "pwio.h"
@@ -21,12 +24,11 @@
{
struct passwd *pw;
pw = (struct passwd *) malloc (sizeof *pw);
pw = CALLOC (1, struct passwd);
if (NULL == pw) {
return NULL;
}
/* The libc might define other fields. They won't be copied. */
memset (pw, 0, sizeof *pw);
pw->pw_uid = pwent->pw_uid;
pw->pw_gid = pwent->pw_gid;
/*@-mustfreeonly@*/
@@ -68,7 +70,8 @@
return pw;
}
void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent)
void
pw_free(/*@only@*/struct passwd *pwent)
{
if (pwent != NULL) {
free (pwent->pw_name);
+198
View File
@@ -0,0 +1,198 @@
/* $OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $ */
/*
* Copyright (c) 2000-2002, 2007, 2010
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <readpassphrase.h>
#ifndef TCSASOFT
#define TCSASOFT 0
#endif
#ifndef _NSIG
#if defined(NSIG)
#define _NSIG NSIG
#else
/* The SIGRTMAX define might be set to a function such as sysconf(). */
#define _NSIG (SIGRTMAX + 1)
#endif
#endif
static volatile sig_atomic_t signo[_NSIG];
static void handler(int);
char *
readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
{
ssize_t nr;
int input, output, save_errno, i, need_restart;
char ch, *p, *end;
struct termios term, oterm;
struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
struct sigaction savetstp, savettin, savettou, savepipe;
/* I suppose we could alloc on demand in this case (XXX). */
if (bufsiz == 0) {
errno = EINVAL;
return(NULL);
}
restart:
for (i = 0; i < _NSIG; i++)
signo[i] = 0;
nr = -1;
save_errno = 0;
need_restart = 0;
/*
* Read and write to /dev/tty if available. If not, read from
* stdin and write to stderr unless a tty is required.
*/
if ((flags & RPP_STDIN) ||
(input = output = open(_PATH_TTY, O_RDWR)) == -1) {
if (flags & RPP_REQUIRE_TTY) {
errno = ENOTTY;
return(NULL);
}
input = STDIN_FILENO;
output = STDERR_FILENO;
}
/*
* Turn off echo if possible.
* If we are using a tty but are not the foreground pgrp this will
* generate SIGTTOU, so do it *before* installing the signal handlers.
*/
if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
memcpy(&term, &oterm, sizeof(term));
if (!(flags & RPP_ECHO_ON))
term.c_lflag &= ~(ECHO | ECHONL);
#ifdef VSTATUS
if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
} else {
memset(&term, 0, sizeof(term));
term.c_lflag |= ECHO;
memset(&oterm, 0, sizeof(oterm));
oterm.c_lflag |= ECHO;
}
/*
* Catch signals that would otherwise cause the user to end
* up with echo turned off in the shell. Don't worry about
* things like SIGXCPU and SIGVTALRM for now.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0; /* don't restart system calls */
sa.sa_handler = handler;
(void)sigaction(SIGALRM, &sa, &savealrm);
(void)sigaction(SIGHUP, &sa, &savehup);
(void)sigaction(SIGINT, &sa, &saveint);
(void)sigaction(SIGPIPE, &sa, &savepipe);
(void)sigaction(SIGQUIT, &sa, &savequit);
(void)sigaction(SIGTERM, &sa, &saveterm);
(void)sigaction(SIGTSTP, &sa, &savetstp);
(void)sigaction(SIGTTIN, &sa, &savettin);
(void)sigaction(SIGTTOU, &sa, &savettou);
if (!(flags & RPP_STDIN))
(void)write(output, prompt, strlen(prompt));
end = buf + bufsiz - 1;
p = buf;
while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
if (p < end) {
if ((flags & RPP_SEVENBIT))
ch &= 0x7f;
if (isalpha((unsigned char)ch)) {
if ((flags & RPP_FORCELOWER))
ch = (char)tolower((unsigned char)ch);
if ((flags & RPP_FORCEUPPER))
ch = (char)toupper((unsigned char)ch);
}
*p++ = ch;
}
}
*p = '\0';
save_errno = errno;
if (!(term.c_lflag & ECHO))
(void)write(output, "\n", 1);
/* Restore old terminal settings and signals. */
if (memcmp(&term, &oterm, sizeof(term)) != 0) {
const int sigttou = signo[SIGTTOU];
/* Ignore SIGTTOU generated when we are not the fg pgrp. */
while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
errno == EINTR && !signo[SIGTTOU])
continue;
signo[SIGTTOU] = sigttou;
}
(void)sigaction(SIGALRM, &savealrm, NULL);
(void)sigaction(SIGHUP, &savehup, NULL);
(void)sigaction(SIGINT, &saveint, NULL);
(void)sigaction(SIGQUIT, &savequit, NULL);
(void)sigaction(SIGPIPE, &savepipe, NULL);
(void)sigaction(SIGTERM, &saveterm, NULL);
(void)sigaction(SIGTSTP, &savetstp, NULL);
(void)sigaction(SIGTTIN, &savettin, NULL);
(void)sigaction(SIGTTOU, &savettou, NULL);
if (input != STDIN_FILENO)
(void)close(input);
/*
* If we were interrupted by a signal, resend it to ourselves
* now that we have restored the signal handlers.
*/
for (i = 0; i < _NSIG; i++) {
if (signo[i]) {
kill(getpid(), i);
switch (i) {
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
need_restart = 1;
}
}
}
if (need_restart)
goto restart;
if (save_errno)
errno = save_errno;
return(nr == -1 ? NULL : buf);
}
static void handler(int s)
{
signo[s] = 1;
}
+47
View File
@@ -0,0 +1,47 @@
/* $OpenBSD: readpassphrase.h,v 1.4 2003/06/03 01:52:39 millert Exp $ */
/*
* Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#ifndef LIBBSD_READPASSPHRASE_H
#define LIBBSD_READPASSPHRASE_H
#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */
#define RPP_ECHO_ON 0x01 /* Leave echo on. */
#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */
#define RPP_FORCELOWER 0x04 /* Force input to lower case. */
#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */
#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */
#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */
#ifdef LIBBSD_OVERLAY
#include <sys/cdefs.h>
#endif
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
char * readpassphrase(const char *, char *, size_t, int);
#ifdef __cplusplus
}
#endif
#endif /* !LIBBSD_READPASSPHRASE_H */
+101
View File
@@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 2001, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2006, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2010, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include "prototypes.h"
#include "defines.h"
static int remove_tree_at (int at_fd, const char *path, bool remove_root)
{
DIR *dir;
const struct dirent *ent;
int dir_fd, rc = 0;
dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (dir_fd < 0) {
return -1;
}
dir = fdopendir (dir_fd);
if (!dir) {
(void) close (dir_fd);
return -1;
}
/*
* Open the source directory and delete each entry.
*/
while ((ent = readdir (dir))) {
struct stat ent_sb;
/*
* Skip the "." and ".." entries
*/
if (strcmp (ent->d_name, ".") == 0 ||
strcmp (ent->d_name, "..") == 0) {
continue;
}
rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW);
if (rc < 0) {
break;
}
if (S_ISDIR (ent_sb.st_mode)) {
/*
* Recursively delete this directory.
*/
if (remove_tree_at (dirfd(dir), ent->d_name, true) != 0) {
rc = -1;
break;
}
} else {
/*
* Delete the file.
*/
if (unlinkat (dirfd(dir), ent->d_name, 0) != 0) {
rc = -1;
break;
}
}
}
(void) closedir (dir);
if (remove_root && (0 == rc)) {
if (unlinkat (at_fd, path, AT_REMOVEDIR) != 0) {
rc = -1;
}
}
return rc;
}
/*
* remove_tree - delete a directory tree
*
* remove_tree() walks a directory tree and deletes all the files
* and directories.
* At the end, it deletes the root directory itself.
*/
int remove_tree (const char *root, bool remove_root)
{
return remove_tree_at (AT_FDCWD, root, remove_root);
}
+132
View File
@@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2003 - 2005, Tomasz Kłoczko
* SPDX-FileCopyrightText: 2007 - 2008, Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ifdef RLOGIN
#ident "$Id$"
#include "prototypes.h"
#include "defines.h"
#include <stdio.h>
#include <pwd.h>
#include <netdb.h>
static struct {
int spd_name;
int spd_baud;
} speed_table[] =
{
{ B50, 50},
{ B75, 75},
{ B110, 110},
{ B134, 134},
{ B150, 150},
{ B200, 200},
{ B300, 300},
{ B600, 600},
{ B1200, 1200},
{ B1800, 1800},
{ B2400, 2400},
{ B4800, 4800},
{ B9600, 9600},
{ B19200, 19200},
{ B38400, 38400},
{ -1, -1}
};
static void
get_remote_string(char *buf, size_t size)
{
for (;;) {
if (read (0, buf, 1) != 1) {
exit (EXIT_FAILURE);
}
if ('\0' == *buf) {
return;
}
--size;
if (size > 0) {
++buf;
}
}
/*NOTREACHED*/
}
int
do_rlogin(const char *remote_host, char *name, size_t namesize, char *term,
size_t termsize)
{
struct passwd *pwd;
char remote_name[32];
char *cp;
unsigned long remote_speed = 9600;
int speed_name = B9600;
int i;
TERMIO termio;
get_remote_string(remote_name, sizeof(remote_name));
get_remote_string(name, namesize);
get_remote_string(term, termsize);
cp = strchr (term, '/');
if (NULL != cp) {
*cp = '\0';
cp++;
if (getulong(cp, &remote_speed) == -1) {
remote_speed = 9600;
}
}
for (i = 0;
( (speed_table[i].spd_baud != remote_speed)
&& (speed_table[i].spd_name != -1));
i++);
if (-1 != speed_table[i].spd_name) {
speed_name = speed_table[i].spd_name;
}
/*
* Put the terminal in cooked mode with echo turned on.
*/
GTTY (0, &termio);
termio.c_iflag |= ICRNL | IXON;
termio.c_oflag |= OPOST | ONLCR;
termio.c_lflag |= ICANON | ECHO | ECHOE;
#ifdef CBAUD
termio.c_cflag = (termio.c_cflag & ~CBAUD) | speed_name;
#else
termio.c_cflag = (termio.c_cflag) | speed_name;
#endif
STTY (0, &termio);
pwd = getpwnam (name); /* local, no need for xgetpwnam */
if (NULL == pwd) {
return 0;
}
/*
* ruserok() returns 0 for success on modern systems, and 1 on
* older ones. If you are having trouble with people logging
* in without giving a required password, THIS is the culprit -
* go fix the #define in config.h.
*/
#ifndef RUSEROK
return 0;
#else
return ruserok (remote_host, pwd->pw_uid == 0,
remote_name, name) == RUSEROK;
#endif
}
#endif /* RLOGIN */
+108
View File
@@ -0,0 +1,108 @@
/*
* SPDX-FileCopyrightText: 2011 , Julian Pidancet
* SPDX-FileCopyrightText: 2011 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#ident "$Id$"
#include <stdio.h>
#include <assert.h>
#include "defines.h"
#include "prototypes.h"
/*@-exitarg@*/
#include "exitcodes.h"
#include "shadowlog.h"
static void change_root (const char* newroot);
/*
* process_root_flag - chroot if given the --root option
*
* This shall be called before accessing the passwd, group, shadow,
* gshadow, useradd's default, login.defs files (non exhaustive list)
* or authenticating the caller.
*
* The audit, syslog, or locale files shall be open before
*/
extern void process_root_flag (const char* short_opt, int argc, char **argv)
{
/*
* Parse the command line options.
*/
int i;
const char *newroot = NULL, *val;
for (i = 0; i < argc; i++) {
val = NULL;
if ( (strcmp (argv[i], "--root") == 0)
|| ((strncmp (argv[i], "--root=", 7) == 0)
&& (val = argv[i] + 7))
|| (strcmp (argv[i], short_opt) == 0)) {
if (NULL != newroot) {
fprintf (log_get_logfd(),
_("%s: multiple --root options\n"),
log_get_progname());
exit (E_BAD_ARG);
}
if (val) {
newroot = val;
} else if (i + 1 == argc) {
fprintf (log_get_logfd(),
_("%s: option '%s' requires an argument\n"),
log_get_progname(), argv[i]);
exit (E_BAD_ARG);
} else {
newroot = argv[++ i];
}
}
}
if (NULL != newroot) {
change_root (newroot);
}
}
static void change_root (const char* newroot)
{
/* Drop privileges */
if ( (setregid (getgid (), getgid ()) != 0)
|| (setreuid (getuid (), getuid ()) != 0)) {
fprintf (log_get_logfd(), _("%s: failed to drop privileges (%s)\n"),
log_get_progname(), strerror (errno));
exit (EXIT_FAILURE);
}
if ('/' != newroot[0]) {
fprintf (log_get_logfd(),
_("%s: invalid chroot path '%s', only absolute paths are supported.\n"),
log_get_progname(), newroot);
exit (E_BAD_ARG);
}
if (access (newroot, F_OK) != 0) {
fprintf(log_get_logfd(),
_("%s: cannot access chroot directory %s: %s\n"),
log_get_progname(), newroot, strerror (errno));
exit (E_BAD_ARG);
}
if (chroot (newroot) != 0) {
fprintf(log_get_logfd(),
_("%s: unable to chroot to directory %s: %s\n"),
log_get_progname(), newroot, strerror (errno));
exit (E_BAD_ARG);
}
if (chdir ("/") != 0) {
fprintf(log_get_logfd(),
_("%s: cannot chdir in chroot directory %s: %s\n"),
log_get_progname(), newroot, strerror (errno));
exit (E_BAD_ARG);
}
}
+13 -11
View File
@@ -1,3 +1,5 @@
#include <config.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
@@ -8,9 +10,12 @@
#include <sys/wait.h>
#include <unistd.h>
#include <lib/prototypes.h>
#include "alloc.h"
#include "run_part.h"
#include "shadowlog_internal.h"
int run_part (char *script_path, const char *name, const char *action)
{
int pid;
@@ -53,25 +58,22 @@ int run_parts (const char *directory, const char *name, const char *action)
}
for (n=0; n<scanlist; n++) {
int path_length;
struct stat sb;
char *s;
struct stat sb;
path_length=strlen(directory) + strlen(namelist[n]->d_name) + 2;
char *s = (char*)malloc(path_length);
if (!s) {
printf ("could not allocate memory\n");
if (asprintf(&s, "%s/%s", directory, namelist[n]->d_name) == -1) {
fprintf(stderr, "could not allocate memory\n");
for (; n<scanlist; n++) {
free (namelist[n]);
free(namelist[n]);
}
free (namelist);
free(namelist);
return (1);
}
snprintf (s, path_length, "%s/%s", directory, namelist[n]->d_name);
execute_result = 0;
if (stat (s, &sb) == -1) {
perror ("stat");
free (s);
free(s);
for (; n<scanlist; n++) {
free (namelist[n]);
}
@@ -83,7 +85,7 @@ int run_parts (const char *directory, const char *name, const char *action)
execute_result = run_part (s, name, action);
}
free (s);
free(s);
if (execute_result!=0) {
fprintf (shadow_logfd,
+452
View File
@@ -0,0 +1,452 @@
/*
* SPDX-FileCopyrightText: Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>
* SPDX-FileCopyrightText: J.T. Conklin <jtc@netbsd.org>
*
* SPDX-License-Identifier: Unlicense
*/
/*
* salt.c - generate a random salt string for crypt()
*
* Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
* it is in the public domain.
*/
#include <config.h>
#ident "$Id$"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "prototypes.h"
#include "defines.h"
#include "getdef.h"
#include "shadowlog.h"
#if (defined CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY && \
CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY)
#define USE_XCRYPT_GENSALT 1
#else
#define USE_XCRYPT_GENSALT 0
#endif
/* Add the salt prefix. */
#define MAGNUM(array,ch) (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0'
#ifdef USE_BCRYPT
/* Use $2b$ as prefix for compatibility with OpenBSD's bcrypt. */
#define BCRYPTMAGNUM(array) (array)[0]=(array)[3]='$',(array)[1]='2',(array)[2]='b',(array)[4]='\0'
#define BCRYPT_SALT_SIZE 22
/* Default number of rounds if not explicitly specified. */
#define B_ROUNDS_DEFAULT 13
/* Minimum number of rounds. */
#define B_ROUNDS_MIN 4
/* Maximum number of rounds. */
#define B_ROUNDS_MAX 31
#endif /* USE_BCRYPT */
#ifdef USE_SHA_CRYPT
/* Fixed salt len for sha{256,512}crypt. */
#define SHA_CRYPT_SALT_SIZE 16
/* Default number of rounds if not explicitly specified. */
#define SHA_ROUNDS_DEFAULT 5000
/* Minimum number of rounds. */
#define SHA_ROUNDS_MIN 1000
/* Maximum number of rounds. */
#define SHA_ROUNDS_MAX 999999999
#endif
#ifdef USE_YESCRYPT
/*
* Default number of base64 characters used for the salt.
* 24 characters gives a 144 bits (18 bytes) salt. Unlike the more
* traditional 128 bits (16 bytes) salt, this 144 bits salt is always
* represented by the same number of base64 characters without padding
* issue, even with a non-standard base64 encoding scheme.
*/
#define YESCRYPT_SALT_SIZE 24
/* Default cost if not explicitly specified. */
#define Y_COST_DEFAULT 5
/* Minimum cost. */
#define Y_COST_MIN 1
/* Maximum cost. */
#define Y_COST_MAX 11
#endif
/* Fixed salt len for md5crypt. */
#define MD5_CRYPT_SALT_SIZE 8
/* Generate salt of size salt_size. */
#define MAX_SALT_SIZE 44
#define MIN_SALT_SIZE 8
/* Maximum size of the generated salt string. */
#define GENSALT_SETTING_SIZE 100
/* local function prototypes */
#if !USE_XCRYPT_GENSALT
static /*@observer@*/const char *gensalt (size_t salt_size);
#endif /* !USE_XCRYPT_GENSALT */
#ifdef USE_SHA_CRYPT
static /*@observer@*/unsigned long SHA_get_salt_rounds (/*@null@*/const int *prefered_rounds);
static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds);
#endif /* USE_SHA_CRYPT */
#ifdef USE_BCRYPT
static /*@observer@*/unsigned long BCRYPT_get_salt_rounds (/*@null@*/const int *prefered_rounds);
static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, unsigned long rounds);
#endif /* USE_BCRYPT */
#ifdef USE_YESCRYPT
static /*@observer@*/unsigned long YESCRYPT_get_salt_cost (/*@null@*/const int *prefered_cost);
static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, unsigned long cost);
#endif /* USE_YESCRYPT */
#ifdef USE_SHA_CRYPT
/* Return the the rounds number for the SHA crypt methods. */
static /*@observer@*/unsigned long SHA_get_salt_rounds (/*@null@*/const int *prefered_rounds)
{
unsigned long rounds;
if (NULL == prefered_rounds) {
long min_rounds = getdef_long ("SHA_CRYPT_MIN_ROUNDS", -1);
long max_rounds = getdef_long ("SHA_CRYPT_MAX_ROUNDS", -1);
if ((-1 == min_rounds) && (-1 == max_rounds)) {
rounds = SHA_ROUNDS_DEFAULT;
}
else {
if (-1 == min_rounds) {
min_rounds = max_rounds;
}
if (-1 == max_rounds) {
max_rounds = min_rounds;
}
if (min_rounds > max_rounds) {
max_rounds = min_rounds;
}
rounds = csrand_interval (min_rounds, max_rounds);
}
} else if (0 == *prefered_rounds) {
rounds = SHA_ROUNDS_DEFAULT;
} else {
rounds = (unsigned long) *prefered_rounds;
}
/* Sanity checks. The libc should also check this, but this
* protects against a rounds_prefix overflow. */
if (rounds < SHA_ROUNDS_MIN) {
rounds = SHA_ROUNDS_MIN;
}
if (rounds > SHA_ROUNDS_MAX) {
rounds = SHA_ROUNDS_MAX;
}
return rounds;
}
/*
* Fill a salt prefix specifying the rounds number for the SHA crypt methods
* to a buffer.
*/
static /*@observer@*/void SHA_salt_rounds_to_buf (char *buf, unsigned long rounds)
{
const size_t buf_begin = strlen (buf);
/* Nothing to do here if SHA_ROUNDS_DEFAULT is used. */
if (rounds == SHA_ROUNDS_DEFAULT) {
return;
}
/*
* Check if the result buffer is long enough.
* We are going to write a maximum of 17 bytes,
* plus one byte for the terminator.
* rounds=XXXXXXXXX$
* 00000000011111111
* 12345678901234567
*/
assert (GENSALT_SETTING_SIZE > buf_begin + 17);
(void) snprintf (buf + buf_begin, 18, "rounds=%lu$", rounds);
}
#endif /* USE_SHA_CRYPT */
#ifdef USE_BCRYPT
/* Return the the rounds number for the BCRYPT method. */
static /*@observer@*/unsigned long BCRYPT_get_salt_rounds (/*@null@*/const int *prefered_rounds)
{
unsigned long rounds;
if (NULL == prefered_rounds) {
long min_rounds = getdef_long ("BCRYPT_MIN_ROUNDS", -1);
long max_rounds = getdef_long ("BCRYPT_MAX_ROUNDS", -1);
if ((-1 == min_rounds) && (-1 == max_rounds)) {
rounds = B_ROUNDS_DEFAULT;
} else {
if (-1 == min_rounds) {
min_rounds = max_rounds;
}
if (-1 == max_rounds) {
max_rounds = min_rounds;
}
if (min_rounds > max_rounds) {
max_rounds = min_rounds;
}
rounds = csrand_interval (min_rounds, max_rounds);
}
} else if (0 == *prefered_rounds) {
rounds = B_ROUNDS_DEFAULT;
} else {
rounds = (unsigned long) *prefered_rounds;
}
/* Sanity checks. */
if (rounds < B_ROUNDS_MIN) {
rounds = B_ROUNDS_MIN;
}
#if USE_XCRYPT_GENSALT
if (rounds > B_ROUNDS_MAX) {
rounds = B_ROUNDS_MAX;
}
#else /* USE_XCRYPT_GENSALT */
/*
* Use 19 as an upper bound for now,
* because musl doesn't allow rounds >= 20.
* If musl ever supports > 20 rounds,
* rounds should be set to B_ROUNDS_MAX.
*/
if (rounds > 19) {
rounds = 19;
}
#endif /* USE_XCRYPT_GENSALT */
return rounds;
}
/*
* Fill a salt prefix specifying the rounds number for the BCRYPT method
* to a buffer.
*/
static /*@observer@*/void BCRYPT_salt_rounds_to_buf (char *buf, unsigned long rounds)
{
const size_t buf_begin = strlen (buf);
/*
* Check if the result buffer is long enough.
* We are going to write three bytes,
* plus one byte for the terminator.
* XX$
* 000
* 123
*/
assert (GENSALT_SETTING_SIZE > buf_begin + 3);
(void) snprintf (buf + buf_begin, 4, "%2.2lu$", rounds);
}
#endif /* USE_BCRYPT */
#ifdef USE_YESCRYPT
/* Return the the cost number for the YESCRYPT method. */
static /*@observer@*/unsigned long YESCRYPT_get_salt_cost (/*@null@*/const int *prefered_cost)
{
unsigned long cost;
if (NULL == prefered_cost) {
cost = getdef_num ("YESCRYPT_COST_FACTOR", Y_COST_DEFAULT);
} else if (0 == *prefered_cost) {
cost = Y_COST_DEFAULT;
} else {
cost = (unsigned long) *prefered_cost;
}
/* Sanity checks. */
if (cost < Y_COST_MIN) {
cost = Y_COST_MIN;
}
if (cost > Y_COST_MAX) {
cost = Y_COST_MAX;
}
return cost;
}
/*
* Fill a salt prefix specifying the cost for the YESCRYPT method
* to a buffer.
*/
static /*@observer@*/void YESCRYPT_salt_cost_to_buf (char *buf, unsigned long cost)
{
const size_t buf_begin = strlen (buf);
/*
* Check if the result buffer is long enough.
* We are going to write four bytes,
* plus one byte for the terminator.
* jXX$
* 0000
* 1234
*/
assert (GENSALT_SETTING_SIZE > buf_begin + 4);
buf[buf_begin + 0] = 'j';
if (cost < 3) {
buf[buf_begin + 1] = 0x36 + cost;
} else if (cost < 6) {
buf[buf_begin + 1] = 0x34 + cost;
} else {
buf[buf_begin + 1] = 0x3b + cost;
}
buf[buf_begin + 2] = cost >= 3 ? 'T' : '5';
buf[buf_begin + 3] = '$';
buf[buf_begin + 4] = '\0';
}
#endif /* USE_YESCRYPT */
#if !USE_XCRYPT_GENSALT
static /*@observer@*/const char *gensalt (size_t salt_size)
{
static char salt[MAX_SALT_SIZE + 6];
bzero(salt, MAX_SALT_SIZE + 6);
assert (salt_size >= MIN_SALT_SIZE &&
salt_size <= MAX_SALT_SIZE);
strcat (salt, l64a (csrand ()));
do {
strcat (salt, l64a (csrand ()));
} while (strlen (salt) < salt_size);
salt[salt_size] = '\0';
return salt;
}
#endif /* !USE_XCRYPT_GENSALT */
/*
* Generate 8 base64 ASCII characters of random salt. If MD5_CRYPT_ENAB
* in /etc/login.defs is "yes", the salt string will be prefixed by "$1$"
* (magic) and pw_encrypt() will execute the MD5-based FreeBSD-compatible
* version of crypt() instead of the standard one.
* Other methods can be set with ENCRYPT_METHOD
*
* The method can be forced with the meth parameter.
* If NULL, the method will be defined according to the ENCRYPT_METHOD
* variable, and if not set according to the MD5_CRYPT_ENAB variable,
* which can both be set inside the login.defs file.
*
* If meth is specified, an additional parameter can be provided.
* * For the SHA256 and SHA512 method, this specifies the number of rounds
* (if not NULL).
* * For the YESCRYPT method, this specifies the cost factor (if not NULL).
*/
/*@observer@*/const char *crypt_make_salt (/*@null@*//*@observer@*/const char *meth, /*@null@*/void *arg)
{
static char result[GENSALT_SETTING_SIZE];
size_t salt_len = MAX_SALT_SIZE;
const char *method;
unsigned long rounds = 0;
bzero(result, GENSALT_SETTING_SIZE);
if (NULL != meth)
method = meth;
else {
method = getdef_str ("ENCRYPT_METHOD");
if (NULL == method) {
method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES";
}
}
if (0 == strcmp (method, "MD5")) {
MAGNUM(result, '1');
salt_len = MD5_CRYPT_SALT_SIZE;
rounds = 0;
#ifdef USE_BCRYPT
} else if (0 == strcmp (method, "BCRYPT")) {
BCRYPTMAGNUM(result);
salt_len = BCRYPT_SALT_SIZE;
rounds = BCRYPT_get_salt_rounds (arg);
BCRYPT_salt_rounds_to_buf (result, rounds);
#endif /* USE_BCRYPT */
#ifdef USE_YESCRYPT
} else if (0 == strcmp (method, "YESCRYPT")) {
MAGNUM(result, 'y');
salt_len = YESCRYPT_SALT_SIZE;
rounds = YESCRYPT_get_salt_cost (arg);
YESCRYPT_salt_cost_to_buf (result, rounds);
#endif /* USE_YESCRYPT */
#ifdef USE_SHA_CRYPT
} else if (0 == strcmp (method, "SHA256")) {
MAGNUM(result, '5');
salt_len = SHA_CRYPT_SALT_SIZE;
rounds = SHA_get_salt_rounds (arg);
SHA_salt_rounds_to_buf (result, rounds);
} else if (0 == strcmp (method, "SHA512")) {
MAGNUM(result, '6');
salt_len = SHA_CRYPT_SALT_SIZE;
rounds = SHA_get_salt_rounds (arg);
SHA_salt_rounds_to_buf (result, rounds);
#endif /* USE_SHA_CRYPT */
} else if (0 != strcmp (method, "DES")) {
fprintf (log_get_logfd(),
_("Invalid ENCRYPT_METHOD value: '%s'.\n"
"Defaulting to DES.\n"),
method);
salt_len = MAX_SALT_SIZE;
rounds = 0;
bzero(result, GENSALT_SETTING_SIZE);
}
#if USE_XCRYPT_GENSALT
/*
* Prepare DES setting for crypt_gensalt(), if result
* has not been filled with anything previously.
*/
if ('\0' == result[0]) {
/* Avoid -Wunused-but-set-variable. */
salt_len = GENSALT_SETTING_SIZE - 1;
rounds = 0;
memset(result, '.', salt_len);
result[salt_len] = '\0';
}
char *retval = crypt_gensalt (result, rounds, NULL, 0);
/* Should not happen, but... */
if (NULL == retval) {
fprintf (log_get_logfd(),
_("Unable to generate a salt from setting "
"\"%s\", check your settings in "
"ENCRYPT_METHOD and the corresponding "
"configuration for your selected hash "
"method.\n"), result);
exit (1);
}
return retval;
#else /* USE_XCRYPT_GENSALT */
/* Check if the result buffer is long enough. */
assert (GENSALT_SETTING_SIZE > strlen (result) + salt_len);
/* Concatenate a pseudo random salt. */
strncat (result, gensalt (salt_len),
GENSALT_SETTING_SIZE - strlen (result) - 1);
return result;
#endif /* USE_XCRYPT_GENSALT */
}

Some files were not shown because too many files have changed in this diff Show More