9On Wed, 5 Feb 2014, Ted Unangst wrote:
> On Wed, Feb 05, 2014 at 12:41, Brad Smith wrote:
> >> 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.
>
> Test cases? The fix here will fix this test case, but it's probably
> one of the more contrived error cases.
There's more needed. There are actually three cases:
1) found a match:
getpwnam(): return non-NULL (Linux and Solaris set errno to 0)
getpwnam_r(): set *pwretp to non-NULL and return 0
2) no match, but no error:
getpwnam(): set errno to zero and return NULL
getpwnam_r(): set *pwretp to NULL and return 0
3) something failed:
getpwnam(): set errno to non-zero-errno-value and return NULL
getpwnam_r(): set *pwretp to NULL and return non-zero-errno-value
On Solaris, getpwnam_r() doesn't change errno; on Linux, getpwnam_r() sets
errno to its return value.
So: we need to
- clear errno in the success and "no match" cases,
- need to set errno in the ERANGE case,
- need to return errno from getpw{uid,nam}_r() on failure, and
- need to preserve errno around calls whose failure doesn't affect the
success or failure of the lookup (e.g., the DB close)
Diff below does that, though I haven't checked the YP lookup closely to
verify whether it might return the errno of the wrong system call on
failure. The one thing this diff does *not* do is set errno to zero when
a match is found. Both Linux and Solaris do that, so maybe we should
too...
Philip
Index: gen/getpwent.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/getpwent.c,v
retrieving revision 1.48
diff -u -p -r1.48 getpwent.c
--- gen/getpwent.c 15 Nov 2013 22:32:55 -0000 1.48
+++ gen/getpwent.c 6 Feb 2014 00:15:46 -0000
@@ -672,8 +672,11 @@ _pwhashbyname(const char *name, char *bu
int r;
len = strlen(name);
- if (len > _PW_NAME_LEN)
+ if (len > _PW_NAME_LEN) {
+ /* can't be present, so just treat as "no match" */
+ errno = 0;
return (NULL);
+ }
bf[0] = _PW_KEYBYNAME;
bcopy(name, &bf[1], MIN(len, _PW_NAME_LEN));
key.data = (u_char *)bf;
@@ -707,7 +710,7 @@ getpwnam_r(const char *name, struct pass
struct passwd **pwretp)
{
struct passwd *pwret = NULL;
- int flags = 0, *flagsp;
+ int flags = 0, *flagsp, saved_errno;
_THREAD_PRIVATE_MUTEX_LOCK(pw);
if (!_pw_db && !__initdb())
@@ -727,14 +730,16 @@ getpwnam_r(const char *name, struct pass
pwret = _pwhashbyname(name, buf, buflen, pw, flagsp);
if (!_pw_stayopen) {
+ saved_errno = errno;
(void)(_pw_db->close)(_pw_db);
_pw_db = NULL;
+ errno = saved_errno;
}
fail:
if (pwretp)
*pwretp = pwret;
_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
- return (pwret ? 0 : 1);
+ return (pwret ? 0 : errno);
}
struct passwd *
@@ -742,8 +747,11 @@ getpwnam(const char *name)
{
struct passwd *pw = NULL;
+ /* getpwnam_r sets errno on failure */
if (getpwnam_r(name, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
pw = NULL;
+ else
+ errno = 0;
return (pw);
}
@@ -752,7 +760,7 @@ getpwuid_r(uid_t uid, struct passwd *pw,
struct passwd **pwretp)
{
struct passwd *pwret = NULL;
- int flags = 0, *flagsp;
+ int flags = 0, *flagsp, saved_errno;
_THREAD_PRIVATE_MUTEX_LOCK(pw);
if (!_pw_db && !__initdb())
@@ -772,14 +780,16 @@ getpwuid_r(uid_t uid, struct passwd *pw,
pwret = _pwhashbyuid(uid, buf, buflen, pw, flagsp);
if (!_pw_stayopen) {
+ saved_errno = errno;
(void)(_pw_db->close)(_pw_db);
_pw_db = NULL;
+ errno = saved_errno;
}
fail:
if (pwretp)
*pwretp = pwret;
_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
- return (pwret ? 0 : 1);
+ return (pwret ? 0 : errno);
}
struct passwd *
@@ -787,8 +797,11 @@ getpwuid(uid_t uid)
{
struct passwd *pw = NULL;
+ /* getpwuid_r sets errno on failure */
if (getpwuid_r(uid, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
pw = NULL;
+ else
+ errno = 0;
return (pw);
}
@@ -851,8 +864,11 @@ __initdb(void)
errno = saved_errno;
return (1);
}
- if (!warned)
+ if (!warned) {
+ saved_errno = errno;
syslog(LOG_ERR, "%s: %m", _PATH_MP_DB);
+ errno = saved_errno;
+ }
warned = 1;
return (0);
}
@@ -863,12 +879,20 @@ __hashpw(DBT *key, char *buf, size_t buf
{
char *p, *t;
DBT data;
+ int ret;
- if ((_pw_db->get)(_pw_db, key, &data, 0))
+ if ((ret = (_pw_db->get)(_pw_db, key, &data, 0))) {
+ if (ret == 1) {
+ /* not found, but not an error */
+ errno = 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++));