Skip to content

Commit

Permalink
Wrap all functions accessing /etc/passwd, /etc/group and /etc/shadow
Browse files Browse the repository at this point in the history
Starting with glibc 2.32 the compat nss module for getpwnam calls
__nss_files_fopen (which is a GLIBC_PRIVATE symbol provided by glibc)
instead of fopen (see 299210c1fa67e2dfb564475986fce11cd33db9ad). This
leads to getpwnam calls accessing /etc/passwd from *outside* the chroot
and as a result programs like adduser do not work correctly anymore
under fakechroot.

Starting with glibc 2.34 the __nss_files_fopen was moved from nss to
libc.so and thus wrapping it with LD_PRELOAD has no affect anymore
(see 6212bb67f4695962748a5981e1b9fea105af74f6).

So now we also wrap all the functions accessing /etc/passwd, /etc/group
and /etc/shadow. This solution will ignore NIS, LDAP or other local files
as potentially configured in /etc/nsswitch.conf.

dex4er#98
  • Loading branch information
josch committed Aug 24, 2022
1 parent 28a2cd0 commit 6bca76f
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 0 deletions.
296 changes: 296 additions & 0 deletions src/passwd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
/*
libfakechroot -- fake chroot environment
Copyright (c) 2010, 2013 Piotr Roszatycki <dexter@debian.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/


#include <config.h>

/*
* Starting with glibc 2.32 the compat nss module for getpwnam calls
* __nss_files_fopen (which is a GLIBC_PRIVATE symbol provided by glibc)
* instead of fopen (see 299210c1fa67e2dfb564475986fce11cd33db9ad). This
* leads to getpwnam calls accessing /etc/passwd from *outside* the chroot
* and as a result programs like adduser do not work correctly anymore
* under fakechroot.
*
* Starting with glibc 2.34 the __nss_files_fopen was moved from nss to
* libc.so and thus wrapping it with LD_PRELOAD has no affect anymore
* (see 6212bb67f4695962748a5981e1b9fea105af74f6).
*
* So now we also wrap all the functions accessing /etc/passwd, /etc/group
* and /etc/shadow. This solution will ignore NIS, LDAP or other local files
* as potentially configured in /etc/nsswitch.conf.
*/

#include <gnu/libc-version.h>
#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <shadow.h>
#include "libfakechroot.h"

/* getpwent, setpwent, endpwent, getpwuid, getpwnam */

static FILE *pw_f;

wrapper(getpwent, struct passwd *, (void))
{
if (!pw_f) pw_f = fopen("/etc/passwd", "rbe");
if (!pw_f) return 0;
return fgetpwent(pw_f);
}

wrapper (getpwent_r, int, (struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp))
{
if (!pw_f) pw_f = fopen("/etc/passwd", "rbe");
if (!pw_f) return 0;
return fgetpwent_r(pw_f, pwbuf, buf, buflen, pwbufp);
}

wrapper(setpwent, void, (void))
{
if (pw_f) fclose(pw_f);
pw_f = 0;
}

wrapper(endpwent, void, (void))
{
if (pw_f) fclose(pw_f);
pw_f = 0;
}

wrapper(getpwuid, struct passwd *, (uid_t uid))
{
debug("getpwuid(\"%ul\")", uid);
FILE *f = fopen("/etc/passwd", "rbe");
if (!f) {
return NULL;
}
struct passwd *res = NULL;
while ((res = fgetpwent(f))) {
if (res->pw_uid == uid)
break;
}
fclose(f);
return res;
}

wrapper(getpwuid_r, int, (uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result))
{
debug("getpwuid_r(\"%ul\")", uid);
FILE *f = fopen("/etc/passwd", "rbe");
if (!f) {
return errno;
}
int res;
while (!(res = fgetpwent_r(f, pwd, buf, buflen, result))) {
if (pwd->pw_uid == uid)
break;
}
fclose(f);
return res;
}

wrapper(getpwnam, struct passwd *, (const char *name))
{
debug("getpwnam(\"%s\")", name);
FILE *f = fopen("/etc/passwd", "rbe");
if (!f) {
return NULL;
}
struct passwd *res = NULL;
while ((res = fgetpwent(f))) {
if (name && !strcmp(name, res->pw_name))
break;
}
fclose(f);
return res;
}

wrapper(getpwnam_r, int, (const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result))
{
debug("getpwnam_r(\"%s\")", name);
FILE *f = fopen("/etc/passwd", "rbe");
if (!f) {
return errno;
}
int res;
while (!(res = fgetpwent_r(f, pwd, buf, buflen, result))) {
if (name && !strcmp(name, pwd->pw_name))
break;
}
fclose(f);
return res;
}

/* getgrent, setgrent, endgrent, getgrgid, getgrnam */

static FILE *gr_f;

wrapper(getgrent, struct group *, (void))
{
if (!gr_f) gr_f = fopen("/etc/group", "rbe");
if (!gr_f) return 0;
return fgetgrent(gr_f);
}

wrapper (getgrent_r, int, (struct group *gbuf, char *buf, size_t buflen, struct group **gbufp))
{
if (!gr_f) gr_f = fopen("/etc/group", "rbe");
if (!gr_f) return 0;
return fgetgrent_r(gr_f, gbuf, buf, buflen, gbufp);
}

wrapper(setgrent, void, (void))
{
if (gr_f) fclose(gr_f);
gr_f = 0;
}

wrapper(endgrent, void, (void))
{
if (gr_f) fclose(gr_f);
gr_f = 0;
}

wrapper(getgrgid, struct group *, (gid_t gid))
{
debug("getgrgid(\"%ul\")", gid);
FILE *f = fopen("/etc/group", "rbe");
if (!f) {
return NULL;
}
struct group *res = NULL;
while ((res = fgetgrent(f))) {
if (res->gr_gid == gid)
break;
}
fclose(f);
return res;
}

wrapper(getgrgid_r, int, (gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result))
{
debug("getgrgid_r(\"%ul\")", gid);
FILE *f = fopen("/etc/group", "rbe");
if (!f) {
return errno;
}
int res;
while (!(res = fgetgrent_r(f, grp, buf, buflen, result))) {
if (grp->gr_gid == gid)
break;
}
fclose(f);
return res;
}

wrapper(getgrnam, struct group *, (const char *name))
{
debug("getgrnam(\"%s\")", name);
FILE *f = fopen("/etc/group", "rbe");
if (!f) {
return NULL;
}
struct group *res = NULL;
while ((res = fgetgrent(f))) {
if (name && !strcmp(name, res->gr_name))
break;
}
fclose(f);
return res;
}

wrapper(getgrnam_r, int, (const char *name, struct group *grp, char *buf, size_t buflen, struct group **result))
{
debug("getgrnam_r(\"%s\")", name);
FILE *f = fopen("/etc/group", "rbe");
if (!f) {
return errno;
}
int res;
while (!(res = fgetgrent_r(f, grp, buf, buflen, result))) {
if (name && !strcmp(name, grp->gr_name))
break;
}
fclose(f);
return res;
}

/* getspent, setspent, endspent, getspnam */

static FILE *sp_f;

wrapper(getspent, struct spwd *, (void))
{
if (!sp_f) sp_f = fopen("/etc/shadow", "rbe");
if (!sp_f) return 0;
return fgetspent(sp_f);
}

wrapper(setspent, void, (void))
{
if (sp_f) fclose(sp_f);
sp_f = 0;
}

wrapper(endspent, void, (void))
{
if (sp_f) fclose(sp_f);
sp_f = 0;
}

wrapper(getspnam, struct spwd *, (const char *name))
{
debug("getspnam(\"%s\")", name);
FILE *f = fopen("/etc/shadow", "rbe");
if (!f) {
return NULL;
}
struct spwd *res = NULL;
while ((res = fgetspent(f))) {
if (name && !strcmp(name, res->sp_namp))
break;
}
fclose(f);
return res;
}

wrapper(getspnam_r, int, (const char *name, struct spwd *spbuf, char *buf, size_t buflen, struct spwd **spbufp))
{
debug("getspnam_r(\"%s\")", name);
FILE *f = fopen("/etc/shadow", "rbe");
if (!f) {
return errno;
}
int res;
while (!(res = fgetspent_r(f, spbuf, buf, buflen, spbufp))) {
if (name && !strcmp(name, spbuf->sp_namp))
break;
}
fclose(f);
return res;
}

#else
typedef int empty_translation_unit;
#endif
1 change: 1 addition & 0 deletions test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ TESTS = \
t/mkstemps.t \
t/mktemp.t \
t/opendir.t \
t/passwd.t \
t/popen.t \
t/posix_spawn.t \
t/posix_spawnp.t \
Expand Down
1 change: 1 addition & 0 deletions test/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ check_PROGRAMS = \
test-mkstemps \
test-mktemp \
test-opendir \
test-passwd \
test-popen \
test-posix_spawn \
test-posix_spawnp \
Expand Down
28 changes: 28 additions & 0 deletions test/src/test-passwd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <errno.h>
#include <stdint.h>
#include <unistd.h>

int main (int argc, char *argv[]) {
struct passwd *pwd;

if (argc != 2) {
fprintf(stderr, "Usage: %s username\n", argv[0]);
exit(EXIT_FAILURE);
}

pwd = getpwnam(argv[1]);
if (pwd == NULL) {
if (errno == 0) {
printf("Not found\n");
} else {
perror("getpwnam");
}
exit(EXIT_FAILURE);
}

printf("%jd\n", (intmax_t)(pwd->pw_uid));
exit(EXIT_SUCCESS);
}
23 changes: 23 additions & 0 deletions test/t/passwd.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh

srcdir=${srcdir:-.}
. $srcdir/common.inc.sh

prepare 4

for chroot in chroot fakechroot; do
if [ $chroot = "chroot" ] && ! is_root; then
skip $(( $tap_plan / 2 )) "not root"
else

t=`$srcdir/$chroot.sh $testtree /bin/test-passwd user 2>&1`
test "$t" = "1337" || not
ok "$chroot uid is" $t

t=`$srcdir/$chroot.sh $testtree getent group user 2>&1`
test "$t" = "user:x:1337:" || not
ok "$chroot getent group user is" $t
fi
done

cleanup
6 changes: 6 additions & 0 deletions test/testtree.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ do
mkdir -p $destdir/$d
done

echo "user:x:1337:1337:user:/home/user:/bin/bash" > $destdir/etc/passwd
echo "root:x:0:" > $destdir/etc/group
echo "user:x:1337:" >> $destdir/etc/group

for d in \
/dev \
/proc
Expand Down Expand Up @@ -64,6 +68,7 @@ for p in \
'/usr/bin/dirname' \
'/usr/bin/env' \
'/usr/bin/find' \
'/usr/bin/getent' \
'/usr/bin/id' \
'/usr/bin/ischroot' \
'/usr/bin/less' \
Expand Down Expand Up @@ -116,6 +121,7 @@ for p in \
'libm.so.*' \
'libncurses.so.*' \
'libncursesw.so.*' \
'libnss_*.so.*' \
'libpcre*.so.*' \
'libpthread.so.*' \
'libreadline.so.*' \
Expand Down

0 comments on commit 6bca76f

Please sign in to comment.