New upstream version 4.15.1
This commit is contained in:
+143
-12
@@ -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
File diff suppressed because it is too large
Load Diff
+114
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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_ */
|
||||
|
||||
@@ -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
@@ -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@*/
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -15,7 +15,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
static struct pam_conv conv = {
|
||||
static const struct pam_conv conv = {
|
||||
SHADOW_PAM_CONVERSATION,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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';
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user