Subject: Re: NetBSD-3 NIS-compat getpwnam()/getpwuid iterate entire map [was Re: 3.0 YP lookup latency]
To: None <jonathan@dsg.stanford.edu>
From: Brian Ginsbach <ginsbach@NetBSD.org>
List: tech-net
Date: 06/21/2006 20:56:06
On Wed, Jun 21, 2006 at 01:02:40PM -0700, jonathan@dsg.stanford.edu wrote:
> 
> In message <20060621190837.GA8936@NetBSD.org>, Brian Ginsbach writes:
> 
> >
> >Where exactly do you see the problem?  I think that _compat_pwscan()
> >is broken.  Specifically the call to _passwdcompat_pwscan() for
> >COMPAT_FULL should probably pass search rather than _PW_KEYBYNUM.
> >Was this your suggested fix?
> 
> 
> Nope. The analysis I emailed to Christos was _compat_pwscan() does not
> meet the contract expected by its callers, as described in the
> function-header comment of _compat_pwscan().

Hmm, I'd say "nope" is a bit harsh.  I just simplified things a bit
(and over looked that it needs the search arguments passed too).

> 
> _compat_pwscan is called to handle "passwd: compat" handling for each
> of getpwent(), getpwnam(), and getpwuid().  Those three callers pass a
> `search' (third-to-last) argument of _PW_KEYBYNUM, _PW_KEYBYNAME, and
> _PW_KEYBYUID, respectively.
> _compat_pwscan is expected to do the appropriate thing. However,
> as part of implementing the "compat" local-passwd redirection for
> 	-
> 	-name
> 	-@netgroup
> 	+
> 	+name
> 	+@netgroup
> 
> entries, _compat_pwscan() uses its compat_state->mode argument to
> determine how to proceed.  In the switch for compat_state->mode ==
> _PW_FULL (a "+" line), _compat_pwscan() is unconditionally calling
> _passwdcompat_pwscan() with _PW_KEYBYNUM.  *THAT* is what forces
> iteration through the entire map, via yp_first()/yp_next(). The
> surrounding state machinery (at the end of the loop; terrible style)
> ensures that when compat_state is set to _PW_FULL, we also call
> _passwdcompat_setpassent(). the for(;;) loop then iterates over the entire compat map.

Like I said _compat_pwscan when state->mode is COMPAT_FULL should NOT
call _passwdcompat_pwscan() with _PW_KEYBYNUM -- yes this is what
forces iteration through the entire map.

> 
> Given that analysis, my suggested fix is to rework the code
> 
>                         switch (state->mode) {
> 
>                         case COMPAT_FULL:
>                                         /* get next user */
>                                 rv = _passwdcompat_pwscan(&cpw,
>                                     cbuf, sizeof(cbuf),
>                                     _PW_KEYBYNUM, NULL, 0);
>                                 
> to instead dispatch on the `search' argument, and handle each of
> _PW_KEYBYNUM, _PW_KEYBYNAME, and _PW_KEYBYUID appropriately.
> 
> For the first case, we follow the current code. For the latter two, we
> have the key for passwd.byname or passwd.byuid, respectively, so w can
> just yp_match(), process the entry if found and update the loop state
> to proceed to the next line.

It should be as simple as changing the above call to
_passwdcompat_pwscan().

                                rv = _passwdcompat_pwscan(&cpw,
                                    cbuf, sizeof(cbuf),
                                    search, name, uid);

No need for anything fancier because _passwdcompat_pwscan() will
do the right thing and dispatch based on search as you suggest.

> 
> The non-"compat" lines shown in nsswitch.conf(5) don't go through
> _compat_pwscan; so Stephen should be able to work around the
> map-enumeration by reworking his nsswitch.conf to not use the
> "passwd" compat".  Hmm, whilst I typed this, Stephen confirmed that my
> workaround masked the problem.

Yes, the problem can be partially worked around.  If one is not
using the +/- user stuff and only the simple + (include everything)
entry.  But the proper way would be to have nsswitch.conf read

  passwd: files nis