On 30/01/14 5:32 AM, Jérémie Courrèges-Anglas wrote:
[email protected] writes:

Synopsis:       Bad return value for getpwnam_r et al
Category:       42
Environment:
        System      : OpenBSD 5.4
        Details     : OpenBSD 5.4 (GENERIC.MP) #41: Tue Jul 30 15:30:02 MDT 2013
                         
[email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP

        Architecture: OpenBSD.amd64
        Machine     : amd64
Description:
        POSIX says "[t]he getpwnam_r() function shall return zero on success
        or if the requested entry was not found and no error has occurred.
        If an error has occurred, an error number shall be returned to
        indicate the error."

        However, OpenBSD returns 0 on success or 1 on failure; it doesn't
        return an error number. Linux, OS X, Solaris, FreeBSD, and NetBSD
        all return an error number, and in particular return ERANGE when the
        provided buffer is too small.

The following diff seems to be enough (at least for pwd.db):

Index: getpwent.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/getpwent.c,v
retrieving revision 1.48
diff -u -p -p -u -r1.48 getpwent.c
--- getpwent.c  15 Nov 2013 22:32:55 -0000      1.48
+++ getpwent.c  30 Jan 2014 10:24:15 -0000
@@ -867,8 +867,10 @@ __hashpw(DBT *key, char *buf, size_t buf
        if ((_pw_db->get)(_pw_db, key, &data, 0))
                return (0);
        p = (char *)data.data;
-       if (data.size > buflen)
+       if (data.size > buflen) {
+               errno = ERANGE;
                return (0);
+       }

        t = buf;
  #define       EXPAND(e)       e = t; while ((*t++ = *p++));


        Background: I was looping over getpwnam_r so I wouldn't have to use
        sysconf() to get _SC_GETPW_R_SIZE_MAX. Linux/glibc has an annoying
        habit of setting some limits to INT_MAX. It doesn't in this case,
        but I'm not confident they won't do something stupid down the line.

How-To-Repeat:
        #include <stdlib.h>
        #include <stdio.h>
        #include <string.h>
        #include <unistd.h>
        #include <pwd.h>


        int main(void) {
                struct passwd ent, *found;
                char *buf = NULL;
                size_t bufsiz = 1;
                int error;

                found = NULL;

                buf = malloc(bufsiz);

                while ((error = getpwnam_r("root", &ent, buf, bufsiz, &found))) 
{
                        fprintf(stderr, "bufsiz:%zu error:%s\n", bufsiz, 
strerror(error));
                                                                                
   ^^^^^
I think you mean errno, not error.

                        bufsiz <<= 1;
                        buf = realloc(buf, bufsiz);
                        found = NULL;
                }

                printf("pw_dir:   %s\n", ent.pw_dir);
                printf("pw_shell: %s\n", ent.pw_shell);

                return 0;
        }

Fix:
        No easy fix. The supporting routines _pwhashbyname, _pwhashbyuid,
        etc in lib/libc/gen/getpwent.c don't propogate error values.

They do (but through errno).  Of course there may be other places where
the internal functions clobber errno before returning to the user code,
but some fixes have been applied already (eg. rev 1.43 of getpwent.c).

Is anyone actually looking into fixing this? This issue was also noticed
when Dovecot was updated to 2.2 and it broke local account authentication.


--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.

Reply via email to