Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -21,6 +21,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 @@ -14,6 +14,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