Message ID | a2d5b334-e02f-b999-6605-c1d4ff114250@redhat.com |
---|---|
State | Not applicable |
Headers |
Received: (qmail 56701 invoked by alias); 3 Aug 2017 19:28:43 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: <libc-alpha.sourceware.org> List-Unsubscribe: <mailto:libc-alpha-unsubscribe-##L=##H@sourceware.org> List-Subscribe: <mailto:libc-alpha-subscribe@sourceware.org> List-Archive: <http://sourceware.org/ml/libc-alpha/> List-Post: <mailto:libc-alpha@sourceware.org> List-Help: <mailto:libc-alpha-help@sourceware.org>, <http://sourceware.org/ml/#faqs> Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 55864 invoked by uid 89); 3 Aug 2017 19:28:41 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-10.5 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM autolearn=ham version=3.3.2 spammy=replies, H*r:192.168.8, H*RU:!192.168.8.100!, H*r:ip*192.168.8.100 X-HELO: mail-qk0-f170.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:organization :message-id:date:user-agent:mime-version:in-reply-to :content-language:content-transfer-encoding; bh=XhopQTsepksB/1POt2A44MZPGKJ89MEm6VU9RvspTPs=; b=WiFGui98+8vwOAoRNHwfwy4Yu8lj9/QSr6AiyD4/xtCL45Ua4ldnLXPRjxdP5UKTYP RDfTFV/9Px8LdWP0OZkj3VZzqVWLBymiHzhBp0Z1YwgFrf28UViZgfAYc/DShniiD3GS FdxKbwRUevmESm7Qyk3FfaiRFSEPRZoVmjjMfcS1HSCCRLkGKA1+VZKtL3B/XBzDtxIT HJz0PzP1rTt22TV8itXq7CHWr0zU+AQ7hnG4G0DHDY1SSSJ3B4QAa00Sdnz/qId8gMIr tyBEfYcGp0WRJ1YQVsACFwTjKkaxRRMeTxf2vr9iDD7F1C//7lZCXFjfxJydxCnMjY1B WJvw== X-Gm-Message-State: AHYfb5jGdolPQrUIQXx/7rlpiiTbdGS1hw5rsiYfMf/DO01fW3hD0kU7 7AtQ5SyZpLpJ6am6PKf78w== X-Received: by 10.55.99.22 with SMTP id x22mr4257702qkb.173.1501788517688; Thu, 03 Aug 2017 12:28:37 -0700 (PDT) Subject: Re: NSS error reporting (bug 20532) To: Florian Weimer <fweimer@redhat.com>, GNU C Library <libc-alpha@sourceware.org> References: <c128f474-992e-c7de-5fae-a2437778c1eb@redhat.com> <87e3d20e-5a51-93ba-6276-0f5305836b2c@redhat.com> <02cabe4c-f2e8-03f7-8307-71cf2aedace5@redhat.com> From: Carlos O'Donell <carlos@redhat.com> Message-ID: <a2d5b334-e02f-b999-6605-c1d4ff114250@redhat.com> Date: Thu, 3 Aug 2017 15:28:35 -0400 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.2.1 MIME-Version: 1.0 In-Reply-To: <02cabe4c-f2e8-03f7-8307-71cf2aedace5@redhat.com> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit |
Commit Message
Carlos O'Donell
Aug. 3, 2017, 7:28 p.m. UTC
On 08/03/2017 01:18 PM, Florian Weimer wrote: > On 08/03/2017 06:49 PM, Carlos O'Donell wrote: >> On 08/03/2017 12:28 PM, Florian Weimer wrote: >>> (L) Carlos added NSS_STATUS_NOTFOUND with *errnop equals 0 as a >>> documented special case to the manual, in commit >>> d4e301c5c65393837e438b6d81feabfbfde7b9c7. This contradicts (A). >>> NSS_STATUS_NOTFOUND is handled implicitly by __nss_next2, which does not >>> have access to the errno value, so I do not understand how this could work. > >> I must assume that (A) is not quite correct. I had two reproducers where >> errno was propagated to the caller, and did result in observable differences? > > Can you dig up the details? And what was fixed in response to this > clarification? https://bugzilla.redhat.com/show_bug.cgi?id=988068 SSSD, installed, client setup, but no server running was returning NSS_STATUS_UNAVAIL / ENOENT, and that results in the ENOENT being propagated to the caller. This was not the intent. The intent was for the SSSD NSS service module to be transparent to the user if sssd was not running. So I had to clarify a class of NSS service module was was intalled, answering replies, lacked a connected data source, but wanted to return a no-error result that would be retried later. The example reproducer was as simple as: #include <sys/types.h> #include <pwd.h> #include <stdio.h> int main(int argc, char* argv[]) { struct passwd pwd; char buf[4096]; int err; struct passwd *res; err = getpwnam_r(argv[1], &pwd, buf, 4096, &res); printf("<%s> err: <%d>\n", argv[1], err); return 0; } With SSSD using status==NSS_STATUS_TRYAGAIN errno==EAGAIN: getgrent() OK group(0) = root getgrent() OK group(1) = bin getgrent() OK group(2) = daemon ... getgrent: Resource temporarily unavailable getgrent error 11 With SSSD using status==NSS_STATUS_UNAVAIL errno==ENOENT: getgrent() OK group(0) = root getgrent() OK group(1) = bin getgrent() OK group(2) = daemon ... getgrent: No such file or directory getgrent error 2 With SSSD using status==NSS_STATUS_NOTFOUND errno==0 (as suggested): getgrent() OK group(0) = root getgrent() OK group(1) = bin getgrent() OK group(2) = daemon getgrent() OK group(3) = sys ... getgrent() OK group(185) = wildfly > Are you sure this wasn't due to a NSS framework bug? > > If errno is indeed propagated, then setting it 0 is probably wrong > because POSIX does not allow setting it to 0. You are absolutely right. It should have been clarified to say "errno is left untouched." This is the patch I worked up during testing: --- Should we have saved and restored errno?
Comments
On 08/03/2017 09:28 PM, Carlos O'Donell wrote: > On 08/03/2017 01:18 PM, Florian Weimer wrote: >> On 08/03/2017 06:49 PM, Carlos O'Donell wrote: >>> On 08/03/2017 12:28 PM, Florian Weimer wrote: >>>> (L) Carlos added NSS_STATUS_NOTFOUND with *errnop equals 0 as a >>>> documented special case to the manual, in commit >>>> d4e301c5c65393837e438b6d81feabfbfde7b9c7. This contradicts (A). >>>> NSS_STATUS_NOTFOUND is handled implicitly by __nss_next2, which does not >>>> have access to the errno value, so I do not understand how this could work. >> >>> I must assume that (A) is not quite correct. I had two reproducers where >>> errno was propagated to the caller, and did result in observable differences? >> >> Can you dig up the details? And what was fixed in response to this >> clarification? > > https://bugzilla.redhat.com/show_bug.cgi?id=988068 > > SSSD, installed, client setup, but no server running was returning > NSS_STATUS_UNAVAIL / ENOENT, and that results in the ENOENT being > propagated to the caller. This was not the intent. The intent was > for the SSSD NSS service module to be transparent to the user if > sssd was not running. So I had to clarify a class of NSS service module > was was intalled, answering replies, lacked a connected data source, > but wanted to return a no-error result that would be retried later. > > The example reproducer was as simple as: > > #include <sys/types.h> > #include <pwd.h> > #include <stdio.h> > > int main(int argc, char* argv[]) { > struct passwd pwd; > char buf[4096]; > int err; > struct passwd *res; > > err = getpwnam_r(argv[1], &pwd, buf, 4096, &res); > > printf("<%s> err: <%d>\n", argv[1], err); > return 0; > } This reproducer does not match the output below. I think there are several issues in play here. (i) NSS_STATUS_UNAVAIL must come with an errno error code on the NSS module interface. If there is no error code, NSS_STATUS_UNAVAIL is likely the wrong result code. (ii) getpwnam_r (and all of get*_r) uses &errno as the errnop pointer, and calls __set_errno explicitly (even to 0). A lot of code relies on the errno update, which is not required by POSIX. (iii) As a result, getpwnam (without _r) does not leave errno untouched on success, which is required to tell the cause of the getpwnam result: error lack of user. (getpwnam follows the readdir interface convention here: Set errno to zero before the call, and if it is still zero, no error happened.) > With SSSD using status==NSS_STATUS_TRYAGAIN errno==EAGAIN: > > getgrent() OK group(0) = root > getgrent() OK group(1) = bin > getgrent() OK group(2) = daemon > ... > getgrent: Resource temporarily unavailable > getgrent error 11 Note: Now we are talking about getgrent. get*ent (in the form of __nss_getent_r) has both bugs (ii) and (iii). For getgrent, the case for readdir-like behavior is even clearer. > It should have been clarified to say "errno is left untouched." Untouched by whom? The NSS module or the framework? The framework currently clobbers errno unconditionally for NSS_STATUS_SUCCESS and NSS_STATUS_NOTFOUND in a few places. > Should we have saved and restored errno? We save and restore errno in the NSS framework for NSS_STATUS_NOTFOUND, so that we get POSIX semantics for errno. We could also pass a local int variable instead of &errno for errnop, and leave it to the NSS module not to clobber errno, but that will result in a lot of code duplication, and the NSS_STATUS_NOTFOUND code is already unambiguous in this regard. I now think we should not try to separate errno and *errnop because so much code relies on errno updates even for the *_r functions (see glob and nscd for examples), and keeping the aliasing (even extending it to getaddrinfo) will increase compatibility with existing NSS Modules. The errno saving cannot happen at the level of the *_r functions because of the ERANGE case, which requires an *errnop update. In particular, what can happen is that we get NSS_STATUS_TRYAGAIN/ERANGE first, followed by NSS_STATUS_NOTFOUND once we retry with a larger buffer. This can happen with nss_files and long comment lines, for instance. Thanks, Florian
On 08/04/2017 05:54 AM, Florian Weimer wrote: > On 08/03/2017 09:28 PM, Carlos O'Donell wrote: >> On 08/03/2017 01:18 PM, Florian Weimer wrote: >>> On 08/03/2017 06:49 PM, Carlos O'Donell wrote: >>>> On 08/03/2017 12:28 PM, Florian Weimer wrote: >>>>> (L) Carlos added NSS_STATUS_NOTFOUND with *errnop equals 0 as a >>>>> documented special case to the manual, in commit >>>>> d4e301c5c65393837e438b6d81feabfbfde7b9c7. This contradicts (A). >>>>> NSS_STATUS_NOTFOUND is handled implicitly by __nss_next2, which does not >>>>> have access to the errno value, so I do not understand how this could work. >>> >>>> I must assume that (A) is not quite correct. I had two reproducers where >>>> errno was propagated to the caller, and did result in observable differences? >>> >>> Can you dig up the details? And what was fixed in response to this >>> clarification? >> >> https://bugzilla.redhat.com/show_bug.cgi?id=988068 >> >> SSSD, installed, client setup, but no server running was returning >> NSS_STATUS_UNAVAIL / ENOENT, and that results in the ENOENT being >> propagated to the caller. This was not the intent. The intent was >> for the SSSD NSS service module to be transparent to the user if >> sssd was not running. So I had to clarify a class of NSS service module >> was was intalled, answering replies, lacked a connected data source, >> but wanted to return a no-error result that would be retried later. >> >> The example reproducer was as simple as: >> >> #include <sys/types.h> >> #include <pwd.h> >> #include <stdio.h> >> >> int main(int argc, char* argv[]) { >> struct passwd pwd; >> char buf[4096]; >> int err; >> struct passwd *res; >> >> err = getpwnam_r(argv[1], &pwd, buf, 4096, &res); >> >> printf("<%s> err: <%d>\n", argv[1], err); >> return 0; >> } > > This reproducer does not match the output below. I took liberties to cut-and-paste from private notes, and I had a different reproducer at the time, but it illustrates the issue. > I think there are several issues in play here. > > (i) NSS_STATUS_UNAVAIL must come with an errno error code on the NSS > module interface. If there is no error code, NSS_STATUS_UNAVAIL is > likely the wrong result code. Correct, UNAVAIL is a system configuration problem, and needs an error code, and this is not a configuration problem, it is simply that the data source is transiently not available. > (ii) getpwnam_r (and all of get*_r) uses &errno as the errnop pointer, > and calls __set_errno explicitly (even to 0). A lot of code relies on > the errno update, which is not required by POSIX. I think this is OK, as long as the NSS framework saves and restores errno if there was no error, making the usage of errno transparent to the user, and semantically equivalent to the POSIX requirements. > (iii) As a result, getpwnam (without _r) does not leave errno untouched > on success, which is required to tell the cause of the getpwnam result: > error lack of user. This is also a bug, and we should save/restore errno, and modify it only in the event of an error, which would leave us with a POSIX correct implementation, because if errno was zero, and we had no error, it would remain zero. > (getpwnam follows the readdir interface convention here: Set errno to > zero before the call, and if it is still zero, no error happened.) Agreed. >> With SSSD using status==NSS_STATUS_TRYAGAIN errno==EAGAIN: >> >> getgrent() OK group(0) = root >> getgrent() OK group(1) = bin >> getgrent() OK group(2) = daemon >> ... >> getgrent: Resource temporarily unavailable >> getgrent error 11 > > Note: Now we are talking about getgrent. > > get*ent (in the form of __nss_getent_r) has both bugs (ii) and (iii). OK. > For getgrent, the case for readdir-like behavior is even clearer. OK. >> It should have been clarified to say "errno is left untouched." > > Untouched by whom? The NSS module or the framework? The framework > currently clobbers errno unconditionally for NSS_STATUS_SUCCESS and > NSS_STATUS_NOTFOUND in a few places. Assertions: * The NSS module refers to an implementation of the NSS services in a dynamically loaded library. When we talk about module we talk only about the code that implements the low-level functions. * The NSS framework refers to the implementation of the API wrapper which knows how to lookup the required NSS services and call their modules. Let me clarify: * The NSS framework should save and restore errno in the event there is no error, and should only set outbound errno to an error when the internal code or NSS service returns a non-zero errno as part of an external API contract. * Internally the NSS framework should start operation with a zero errno (ignoring whatever value the user set). This zero errno is important such that NSS services can decide internally if errors occurred or not. For example we could set errno to zero, call the NSS service module function, and observe if it failed or not This is overly simplistic I think, but I'm trying to explain and expand my expectations of the interface as I've learned them over the years. We should continue to flesh this out until we understand all the cases. >> Should we have saved and restored errno? > > We save and restore errno in the NSS framework for NSS_STATUS_NOTFOUND, > so that we get POSIX semantics for errno. I think that should be expanded to always save and restore errno, to isolate the framework and service from user-set non-zero errno values. > We could also pass a local int variable instead of &errno for errnop, > and leave it to the NSS module not to clobber errno, but that will > result in a lot of code duplication, and the NSS_STATUS_NOTFOUND code is > already unambiguous in this regard. I now think we should not try to > separate errno and *errnop because so much code relies on errno updates > even for the *_r functions (see glob and nscd for examples), and keeping > the aliasing (even extending it to getaddrinfo) will increase > compatibility with existing NSS Modules. I agree, but I also think this will not solve all of our problems. Keep in mind that on the horizon we have static/dynamic namespace issues coming, which mean that at the boundary between a static namespace and the dynamic namespace we will need some new API to help us carry the global data, like errno, across the boundary. If such an API served dual duty to isolate errno, then it might be a win-win. In NSS we are in a position where the NSS module boundary happens to be the same static/dynamic boundary case we want to fix later. > The errno saving cannot happen at the level of the *_r functions because > of the ERANGE case, which requires an *errnop update. In particular, > what can happen is that we get NSS_STATUS_TRYAGAIN/ERANGE first, > followed by NSS_STATUS_NOTFOUND once we retry with a larger buffer. > This can happen with nss_files and long comment lines, for instance. The level of the *_r functions is nss/getXXbyYY_r.c, and I don't see why we could not intelligently save and restore errno at that level? Given your example it would look like this, and some of this is defensive errno saving because we are going to have flawed service modules that set errno explicitly. nss/getXXbyYY_r.c * save errno. * set errno to zero * resolve service to nss_files * call nss_files low-level functions: e.g. _nss_files_getpwnam_r (nss_files/files-pwd.c) ... and get back NSS_STATUS_TRYAGAIN/ERANGE. * Since ERANGE we flag that error has occured * if (errno == 0) then restore errno. nss/getXXbyYY.c * call reentrant version In the non-reentrant version, either errno is seen as unmodified in the event of success, or errno is set to the error returned by the underlying NSS module. In this kind of defensive design we can tolerate incorrectly written NSS modules that write 0 to errno without ruining the POSIX requirements that errno be unmodified until the next function modifies it according to its API contract. We could also detect modules setting errno to zero by setting a non-zero errno and seeing what fails. In summary ========== - My opinion is that we need to focus on some high-level design goals and see if we can apply those to the framework and modules with success. - How do we come up with the high-level design goals? Probably a combination of looking at the flaws you drafted, and the API contracts we have (Zack's suggestion), and harmonizing those in some way.
On 08/04/2017 03:52 PM, Carlos O'Donell wrote: > The level of the *_r functions is nss/getXXbyYY_r.c, and I don't see why > we could not intelligently save and restore errno at that level? > > Given your example it would look like this, and some of this is defensive > errno saving because we are going to have flawed service modules that > set errno explicitly. > > nss/getXXbyYY_r.c > * save errno. > * set errno to zero > * resolve service to nss_files > * call nss_files low-level functions: e.g. _nss_files_getpwnam_r (nss_files/files-pwd.c) > ... and get back NSS_STATUS_TRYAGAIN/ERANGE. > * Since ERANGE we flag that error has occured > * if (errno == 0) then restore errno. How would you pass ERANGE to the caller? For getpwnam, the sequence could be: Call getpwnam_r. Get back ERANGE error. Allocate larger buffer. Call getpwnam_r again. Get back result. Return to caller with success. If the second getpwnam_r saved and restored errno, we'll return ERANGE (or whatever malloc set) to the caller, not the original errno value. Florian
On 08/04/2017 10:08 AM, Florian Weimer wrote: > On 08/04/2017 03:52 PM, Carlos O'Donell wrote: >> The level of the *_r functions is nss/getXXbyYY_r.c, and I don't see why >> we could not intelligently save and restore errno at that level? >> >> Given your example it would look like this, and some of this is defensive >> errno saving because we are going to have flawed service modules that >> set errno explicitly. >> >> nss/getXXbyYY_r.c >> * save errno. >> * set errno to zero >> * resolve service to nss_files >> * call nss_files low-level functions: e.g. _nss_files_getpwnam_r (nss_files/files-pwd.c) >> ... and get back NSS_STATUS_TRYAGAIN/ERANGE. >> * Since ERANGE we flag that error has occured >> * if (errno == 0) then restore errno. > > How would you pass ERANGE to the caller? > > For getpwnam, the sequence could be: > > Call getpwnam_r. > Get back ERANGE error. > Allocate larger buffer. > Call getpwnam_r again. > Get back result. > Return to caller with success. > > If the second getpwnam_r saved and restored errno, we'll return ERANGE > (or whatever malloc set) to the caller, not the original errno value. We stop setting errno in nss/getXXbyYY_r.c, because none of the reentrant versions are supposed to set errno, they are supposed to return the error as their result. This might not work easily if other more complex APIs use the *_r versions and need errno set. nss/getXXbyYY_r.c * save errno. * set errno to zero * resolve service to nss_files * call nss_files low-level functions: e.g. _nss_files_getpwnam_r (nss_files/files-pwd.c) ... and get back NSS_STATUS_TRYAGAIN/ERANGE. * save result. * restore errno. * return result. nss/getXXbyYY.c * call reentrant version * save result * if (result == ERANGE) then try again with larger buffer. Note: errno remains 0 the whole time (except within the *_r function impl) * if result != 0 then errno = result
On 08/04/2017 04:38 PM, Carlos O'Donell wrote: > We stop setting errno in nss/getXXbyYY_r.c, because none of the reentrant > versions are supposed to set errno, they are supposed to return the error > as their result. This might not work easily if other more complex APIs use > the *_r versions and need errno set. > > nss/getXXbyYY_r.c > * save errno. > * set errno to zero > * resolve service to nss_files > * call nss_files low-level functions: e.g. _nss_files_getpwnam_r (nss_files/files-pwd.c) > ... and get back NSS_STATUS_TRYAGAIN/ERANGE. > * save result. > * restore errno. > * return result. > > nss/getXXbyYY.c > * call reentrant version > * save result > * if (result == ERANGE) then try again with larger buffer. > Note: errno remains 0 the whole time (except within the *_r function impl) > * if result != 0 then errno = result I'm afraid this is not possible. A pre-POSIX version of getpwnam_r set errno and returned a flag (just like most non-pthread functions). Here is an excerpt from nscd: static int lookup (int type, union keytype key, struct passwd *resultbufp, char *buffer, size_t buflen, struct passwd **pwd) { if (type == GETPWBYNAME) return __getpwnam_r (key.v, resultbufp, buffer, buflen, pwd); else return __getpwuid_r (key.u, resultbufp, buffer, buflen, pwd); } … while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0 && (errval = errno) == ERANGE) Here is another excerpt from glob: while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p) != 0) { if (errno != ERANGE) { p = NULL; break; } wordexp has the same issue: while ((result = __getpwnam_r (user, &pwd, buffer, buflen, &tpwd)) != 0 && errno == ERANGE) Here's an example from OpenJDK: errno = 0; #ifdef __solaris__ RESTARTABLE_RETURN_PTR(getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen), g); #else RESTARTABLE(getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen, &g), res); #endif retry = 0; if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') { /* not found or error */ if (errno == ERANGE) { In short, if we made this change, way too much code would break. Thanks, Florian
On 08/04/2017 11:08 AM, Florian Weimer wrote:
> In short, if we made this change, way too much code would break.
I half expected this answer, which is why my original suggestion had
save/restore errno and then modify it in the outbound case.
We have a decision to make, and I would err on the side of caution,
which means we have to break POSIX requirements here, and accept that
getpwnam_r modifies errno in the event of an error even if POSIX says
it doesn't, which means we change errno when the user isn't expecting.
At least initially I think that saving/restoring errno is thew way to
go here to provide some level of isolation.
On 08/10/2017 07:39 PM, Carlos O'Donell wrote: > On 08/04/2017 11:08 AM, Florian Weimer wrote: >> In short, if we made this change, way too much code would break. > > I half expected this answer, which is why my original suggestion had > save/restore errno and then modify it in the outbound case. > > We have a decision to make, and I would err on the side of caution, > which means we have to break POSIX requirements here, and accept that > getpwnam_r modifies errno in the event of an error even if POSIX says > it doesn't, which means we change errno when the user isn't expecting. I think POSIX allows us to clobber errno. The conformance issue arises when we set errno to 0 on success. I'm not sure if we have to do this for backwards compatibility. Thanks, Florian
diff -urN sssd-1.11.6/src/sss_client/nss_group.c sssd-1.11.6.mod/src/sss_client/nss_group.c --- sssd-1.11.6/src/sss_client/nss_group.c 2014-06-03 10:31:33.000000000 -0400 +++ sssd-1.11.6.mod/src/sss_client/nss_group.c 2014-09-10 12:21:52.330685026 -0400 @@ -539,6 +539,11 @@ if (nret != NSS_STATUS_SUCCESS) { errno = errnop; } + /* Always pretend we have no data. */ + if (nret == NSS_STATUS_UNAVAIL) { + nret = NSS_STATUS_NOTFOUND; + errno = 0; + } sss_nss_unlock(); return nret; @@ -639,6 +644,11 @@ if (nret != NSS_STATUS_SUCCESS) { errno = errnop; } + /* Always pretend we have no data. */ + if (nret == NSS_STATUS_UNAVAIL) { + nret = NSS_STATUS_NOTFOUND; + errno = 0; + } sss_nss_unlock(); return nret;