diff --git a/src/passwd.c b/src/passwd.c new file mode 100644 index 00000000..d4cee86c --- /dev/null +++ b/src/passwd.c @@ -0,0 +1,296 @@ +/* + libfakechroot -- fake chroot environment + Copyright (c) 2010, 2013 Piotr Roszatycki + + 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 + +/* + * 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 +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32) + +#include +#include +#include +#include +#include +#include +#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 diff --git a/test/Makefile.am b/test/Makefile.am index 0021b0a1..a1ec743d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 \ diff --git a/test/src/Makefile.am b/test/src/Makefile.am index 5f5fde8d..594a8e0f 100644 --- a/test/src/Makefile.am +++ b/test/src/Makefile.am @@ -15,6 +15,7 @@ check_PROGRAMS = \ test-mkstemps \ test-mktemp \ test-opendir \ + test-passwd \ test-popen \ test-posix_spawn \ test-posix_spawnp \ diff --git a/test/src/test-passwd.c b/test/src/test-passwd.c new file mode 100644 index 00000000..fb9c8c4c --- /dev/null +++ b/test/src/test-passwd.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/test/t/passwd.t b/test/t/passwd.t new file mode 100644 index 00000000..5c3414e0 --- /dev/null +++ b/test/t/passwd.t @@ -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 diff --git a/test/testtree.sh b/test/testtree.sh index ee35fc26..d857a195 100755 --- a/test/testtree.sh +++ b/test/testtree.sh @@ -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 @@ -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' \ @@ -116,6 +121,7 @@ for p in \ 'libm.so.*' \ 'libncurses.so.*' \ 'libncursesw.so.*' \ + 'libnss_*.so.*' \ 'libpcre*.so.*' \ 'libpthread.so.*' \ 'libreadline.so.*' \