Possible issue with rpc.idmapd and NFSv4.

Kevin Coffman kwc at citi.umich.edu
Thu Dec 13 12:28:02 EST 2007


On Dec 13, 2007 12:10 PM, J. Bruce Fields <bfields at fieldses.org> wrote:
> On Thu, Dec 13, 2007 at 12:05:18PM -0500, Nathan Patwardhan wrote:
> > On Dec 12, 2007 8:06 PM, Nathan Patwardhan <noopy.org at gmail.com> wrote:
> > > On Dec 12, 2007 7:45 PM, Nathan Patwardhan <noopy.org at gmail.com> wrote:
> > > > > So you've determined that nfs4_gid_to_name is returning non-zero when
> > > > > given 600 as the gid?  Then the bug is probably in libnfsidmap.
> > >
> > > b is correct.  The issue is in libnfsidmap.  I installed
> > > libnfsidmap-0.20 on our dev box just for kicks and the group issue
> > > seems to have gone away.
> >
> > I looked into the issue a little bit more last night and this morning.
> >  The problem seems specific to nss.c and appears to exist in
> > libnfsidmap-0.12 through libnfsidmap-0.19 (or thereabouts).
> >
> > Specifically, there's a syconf(_SC_GETGR_R_SIZE_MAX) call in nss.c
> > that results in a buffer that's never resize if ERANGE.
>
> That sounds exactly right.  Un, in fact, here's where that was found
> before:
>
>         http://linux-nfs.org/pipermail/nfsv4/2007-April/005924.html
>
> but without the attached patch.  It might also be possible to pull the
> same patch out of CVS.
>
> > I've written a patch for libnfsidmap-0.12 against libnfsidmap-0.20
> > that seems to correct this issue, but at this point maybe such a patch
> > is useless since libnfsidmap-0.12 is rather out of date.
>
> > Please advise.
>
> We don't maintain separate branches for older versions, so it's up to
> the distributor to either apply this patch or upgrade to 0.20.
>
> --b.

There were a couple of significant changes between 0.12 and 0.20.
Here is the cvs diff of them.  I don't know if this would apply
cleanly to 0.12.  (I can send as attachment if mailer destroys it.)

===================================================================
RCS file: /cvs/nfsv4/libnfsidmap/nss.c,v
retrieving revision 1.6
retrieving revision 1.10
diff -u -r1.6 -r1.10
--- nss.c       24 Feb 2006 14:29:27 -0000      1.6
+++ nss.c       5 Feb 2007 16:25:00 -0000       1.10
@@ -99,16 +99,25 @@
        struct group grbuf;
        char *buf;
        size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
-       int err = -ENOMEM;
+       int err;

-       buf = malloc(buflen);
-       if (!buf)
-               goto out;
        if (domain == NULL)
                domain = get_default_domain();
-       err = -getgrgid_r(gid, &grbuf, buf, buflen, &gr);
-       if (gr == NULL)
-               err = -ENOENT;
+
+       do {
+               err = -ENOMEM;
+               buf = malloc(buflen);
+               if (!buf)
+                       goto out;
+               err = -getgrgid_r(gid, &grbuf, buf, buflen, &gr);
+               if (gr == NULL && !err)
+                       err = -ENOENT;
+               if (err == -ERANGE) {
+                       buflen *= 2;
+                       free(buf);
+               }
+       } while (err == -ERANGE);
+
        if (err)
                goto out_buf;
        err = write_name(name, gr->gr_name, domain, len);
@@ -127,11 +136,16 @@
        int len;

        c = strchr(name, '@');
-       if (!c)
+       if (c == NULL && domain != NULL)
                goto out;
-       if (domain && strcmp(c + 1, domain) != 0)
-               goto out;
-       len = c - name;
+       if (c == NULL && domain == NULL) {
+               len = strlen(name) + 1;
+       } else {
+               if (domain && strcmp(c + 1, domain) != 0)
+                       goto out;
+               len = c - name;
+       }
+
        l = malloc(len + 1);
        if (l == NULL)
                goto out;
@@ -160,21 +174,27 @@

        err = EINVAL;
        localname = strip_domain(name, domain);
+       IDMAP_LOG(4, ("nss_getpwnam: name '%s' domain '%s': "
+                 "resulting localname '%s'\n", name, domain, localname));
        if (localname == NULL) {
                IDMAP_LOG(0, ("nss_getpwnam: name '%s' does not map "
-                       "into domain '%s'\n", name, domain));
+                       "into domain '%s'\n", name,
+                       domain ? domain : "<not-provided>"));
                goto err_free_buf;
        }

        err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+       if (pw == NULL && domain != NULL)
+               IDMAP_LOG(0,
+                       ("nss_getpwnam: name '%s' not found in domain '%s'\n",
+                       localname, domain));
        free(localname);
-       if (err == 0) {
+       if (err == 0 && pw != NULL) {
                *err_p = 0;
-               return &buf->pwbuf;
+               return pw;
+       } else if (err == 0 && pw == NULL) {
+               err = ENOENT;
        }
-       IDMAP_LOG(0,
-               ("nss_getpwnam: name '%s' not found in domain '%s'\n",
-               localname, domain));

 err_free_buf:
        free(buf);
@@ -187,7 +207,7 @@
 {
        struct passwd *pw = NULL;
        char *domain;
-       int err = -EINVAL;
+       int err = -ENOENT;

        domain = get_default_domain();
        pw = nss_getpwnam(name, domain, &err);
@@ -195,6 +215,7 @@
                goto out;
        *uid = pw->pw_uid;
        free(pw);
+       err = 0;
 out:
        return err;
 }
@@ -205,26 +226,34 @@
        struct group grbuf;
        char *buf, *localname, *domain;
        size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
-       int err = -ENOMEM;
+       int err = -EINVAL;

-       buf = malloc(buflen);
-       if (!buf)
-               goto out;
-       err = -EINVAL;
        domain = get_default_domain();
        localname = strip_domain(name, domain);
        if (!localname)
-               goto out_buf;
-       err = -getgrnam_r(localname, &grbuf, buf, buflen, &gr);
-       if (gr == NULL)
-               err = -ENOENT;
+               goto out;
+
+       do {
+               err = -ENOMEM;
+               buf = malloc(buflen);
+               if (!buf)
+                       goto out_name;
+               err = -getgrnam_r(localname, &grbuf, buf, buflen, &gr);
+               if (gr == NULL && !err)
+                       err = -ENOENT;
+               if (err == -ERANGE) {
+                       buflen *= 2;
+                       free(buf);
+               }
+       } while (err == -ERANGE);
+
        if (err)
-               goto out_name;
+               goto out_buf;
        *gid = gr->gr_gid;
-out_name:
-       free(localname);
 out_buf:
        free(buf);
+out_name:
+       free(localname);
 out:
        return err;
 }
@@ -235,12 +264,15 @@
        struct passwd *pw;
        int err = 0;

-       if (strcmp(secname, "krb5") != 0)
+       if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
                return -EINVAL;
        /* XXX: not quite right?  Need to know default realm? */
+       /* XXX: this should call something like getgssauthnam instead? */
        pw = nss_getpwnam(princ, NULL, &err);
-       if (pw == NULL)
+       if (pw == NULL) {
+               err = -ENOENT;
                goto out;
+       }
        *uid = pw->pw_uid;
        *gid = pw->pw_gid;
        free(pw);
@@ -254,12 +286,15 @@
        struct passwd *pw;
        int ret = -EINVAL;

-       if (strcmp(secname, "krb5") != 0)
+       if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
                goto out;
        /* XXX: not quite right?  Need to know default realm? */
+       /* XXX: this should call something like getgssauthnam instead? */
        pw = nss_getpwnam(princ, NULL, &ret);
-       if (pw == NULL)
+       if (pw == NULL) {
+               ret = -ENOENT;
                goto out;
+       }
        if (getgrouplist(pw->pw_name, pw->pw_gid, groups, ngroups) < 0)
                ret = -ERANGE;
        free(pw);


More information about the NFSv4 mailing list