Hi folks,

we had lots of trouble with rpm's user and group handling in
our kiwi system (which creates bootable ISOs). kiwi uses
rpm --root to install the selected rpms into a directory,
somehow we sometimes got wrong userids and groupids for
files.

It turns out the glibc and nscd are to blame. There seems
to be no way to tell glibc to stop using nscd if it created
a connection for some lookup. rpm always does lookups on
startup, it has to get the home directory of the caller for
example.

Even if nscd is not started glibc's nss functions also cache
their setting, so if you use some exotic nss module for
authentification, it'll still be used after the chroot().

At some point I basically gave up and wrote the attached
patch. It changes rpmugUid/rpmugGid to do the lookup
itself if a chroot() was done. I know it is a bit of a hack,
but I have not found another way.

The patch also
- fixes the cache usage. lastUnameLen/lastGnameLen were never
  set, so the code *always* did a getpwuid/gid call!
- renames lastU/GnameLen to lastU/GnameAlloced in
  rpmugU/Gname, so that it is consistent with the
  rpmugU/Gid function.

Even if you do not want the safe_lookup() part, having a
rpmugChroot() function that calls endpwent()/endgrent() and
drops the local caches if a chroot is done seems like a
good idea.

Cheers,
  Michael.

-- 
Michael Schroeder                                   m...@suse.de
SUSE LINUX Products GmbH,  GF Jeff Hawn, HRB 16746 AG Nuernberg
main(_){while(_=~getchar())putchar(~_-1/(~(_|32)/13*2-11)*13);}
--- ./lib/rpmchroot.c.orig	2011-05-12 08:26:10.000000000 +0000
+++ ./lib/rpmchroot.c	2011-05-12 08:28:32.000000000 +0000
@@ -66,6 +66,7 @@ int rpmChrootIn(void)
     } else if (rootState.chrootDone == 0) {
 	if (chdir("/") == 0 && chroot(rootState.rootDir) == 0) {
 	    rootState.chrootDone = 1;
+	    rpmugChroot(1);
 	} else {
 	    rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n"));
 	    rc = -1;
@@ -91,6 +92,7 @@ int rpmChrootOut(void)
     } else if (rootState.chrootDone == 1) {
 	if (chroot(".") == 0 && fchdir(rootState.cwd) == 0) {
 	    rootState.chrootDone = 0;
+	    rpmugChroot(0);
 	} else {
 	    rpmlog(RPMLOG_ERR, _("Unable to restore root directory: %m\n"));
 	    rc = -1;
--- ./lib/rpmug.c.orig	2011-05-12 08:13:52.000000000 +0000
+++ ./lib/rpmug.c	2011-05-12 08:33:28.000000000 +0000
@@ -35,6 +35,47 @@ const char * rpmugStashStr(const char *s
     return ret;
 }
 
+#if defined(__GLIBC__)
+
+static int inchroot;
+
+/*
+ * Unfortunatelly glibc caches nss/nscd data and there is no
+ * good way to flush those caches when we did a chroot(). Thus
+ * we need to parse /etc/passwd and /etc/group ourselfs.
+ */
+static int safe_lookup(const char * file, const char * name)
+{
+    FILE *fp;
+    int l;
+    char buf[4096], *p;
+
+    if (!name || !*name)
+	return -1;
+    l = strlen(name);
+    if ((fp = fopen(file, "r")) == 0)
+	return -1;
+    while ((p = fgets(buf, sizeof(buf), fp)) != 0) {
+	if (*p == '#')
+	    continue;
+	while (*p && (*p == ' ' || *p == '\t'))
+	    p++;
+	if (strncmp(p, name, l) != 0 || p[l] != ':')
+	    continue;
+	p = strchr(p + l + 1, ':');
+	if (!p)
+	    continue;
+	fclose(fp);
+	p++;
+	while (*p && (*p == ' ' || *p == '\t'))
+	    p++;
+	return atoi(p);
+    }
+    fclose(fp);
+    return -1;
+}
+#endif
+
 /* 
  * These really ought to use hash tables. I just made the
  * guess that most files would be owned by root or the same person/group
@@ -68,17 +109,28 @@ int rpmugUid(const char * thisUname, uid
 	    lastUnameAlloced = thisUnameLen + 10;
 	    lastUname = xrealloc(lastUname, lastUnameAlloced);	/* XXX memory leak */
 	}
-	strcpy(lastUname, thisUname);
 
-	pwent = getpwnam(thisUname);
-	if (pwent == NULL) {
-	    /* FIX: shrug */
-	    endpwent();
+#if defined(__GLIBC__)
+	if (inchroot) {
+	    int uid =  safe_lookup("/etc/passwd", thisUname);
+	    if (uid < 0)
+		return -1;
+	    lastUid = uid;
+	} else
+#endif
+	{
 	    pwent = getpwnam(thisUname);
-	    if (pwent == NULL) return -1;
+	    if (pwent == NULL) {
+		/* FIX: shrug */
+		endpwent();
+		pwent = getpwnam(thisUname);
+		if (pwent == NULL) return -1;
+	    }
+	    lastUid = pwent->pw_uid;
 	}
 
-	lastUid = pwent->pw_uid;
+	strcpy(lastUname, thisUname);
+	lastUnameLen = thisUnameLen;
     }
 
     *uid = lastUid;
@@ -111,18 +163,29 @@ int rpmugGid(const char * thisGname, gid
 	    lastGnameAlloced = thisGnameLen + 10;
 	    lastGname = xrealloc(lastGname, lastGnameAlloced);	/* XXX memory leak */
 	}
-	strcpy(lastGname, thisGname);
 
-	grent = getgrnam(thisGname);
-	if (grent == NULL) {
-	    /* FIX: shrug */
-	    endgrent();
+#if defined(__GLIBC__)
+	if (inchroot) {
+	    int gid =  safe_lookup("/etc/group", thisGname);
+	    if (gid < 0)
+		return -1;
+	    lastGid = gid;
+	} else
+#endif
+	{
 	    grent = getgrnam(thisGname);
 	    if (grent == NULL) {
-		return -1;
+		/* FIX: shrug */
+		endgrent();
+		grent = getgrnam(thisGname);
+		if (grent == NULL) {
+		    return -1;
+		}
 	    }
+	    lastGid = grent->gr_gid;
 	}
-	lastGid = grent->gr_gid;
+	strcpy(lastGname, thisGname);
+	lastGnameLen = thisGnameLen;
     }
 
     *gid = lastGid;
@@ -134,7 +197,7 @@ const char * rpmugUname(uid_t uid)
 {
     static uid_t lastUid = (uid_t) -1;
     static char * lastUname = NULL;
-    static size_t lastUnameLen = 0;
+    static size_t lastUnameAlloced = 0;
 
     if (uid == (uid_t) -1) {
 	lastUid = (uid_t) -1;
@@ -151,9 +214,9 @@ const char * rpmugUname(uid_t uid)
 
 	lastUid = uid;
 	len = strlen(pwent->pw_name);
-	if (lastUnameLen < len + 1) {
-	    lastUnameLen = len + 20;
-	    lastUname = xrealloc(lastUname, lastUnameLen);
+	if (lastUnameAlloced < len + 1) {
+	    lastUnameAlloced = len + 20;
+	    lastUname = xrealloc(lastUname, lastUnameAlloced);
 	}
 	strcpy(lastUname, pwent->pw_name);
 
@@ -165,7 +228,7 @@ const char * rpmugGname(gid_t gid)
 {
     static gid_t lastGid = (gid_t) -1;
     static char * lastGname = NULL;
-    static size_t lastGnameLen = 0;
+    static size_t lastGnameAlloced = 0;
 
     if (gid == (gid_t) -1) {
 	lastGid = (gid_t) -1;
@@ -182,9 +245,9 @@ const char * rpmugGname(gid_t gid)
 
 	lastGid = gid;
 	len = strlen(grent->gr_name);
-	if (lastGnameLen < len + 1) {
-	    lastGnameLen = len + 20;
-	    lastGname = xrealloc(lastGname, lastGnameLen);
+	if (lastGnameAlloced < len + 1) {
+	    lastGnameAlloced = len + 20;
+	    lastGname = xrealloc(lastGname, lastGnameAlloced);
 	}
 	strcpy(lastGname, grent->gr_name);
 
@@ -200,3 +263,16 @@ void rpmugFree(void)
     rpmugGname(-1);
     strStash = strCacheFree(strStash);
 }
+
+void rpmugChroot(int in)
+{
+    /* tell libc to drop caches / file descriptors */
+    endpwent();
+    endgrent();
+    /* drop our own caches */
+    rpmugUid(NULL, NULL);
+    rpmugGid(NULL, NULL);
+#if defined(__GLIBC__)
+    inchroot = in;
+#endif
+}
--- ./lib/rpmug.h.orig	2011-05-12 08:13:52.000000000 +0000
+++ ./lib/rpmug.h	2011-05-12 08:26:56.000000000 +0000
@@ -15,4 +15,6 @@ const char * rpmugGname(gid_t gid);
 
 void rpmugFree(void);
 
+void rpmugChroot(int in);
+
 #endif /* _RPMUG_H */
_______________________________________________
Rpm-maint mailing list
Rpm-maint@lists.rpm.org
http://lists.rpm.org/mailman/listinfo/rpm-maint

Reply via email to