Bug 1491030 - libnfsidmap sss.so plugin MUST always use fully qualified names [NEEDINFO]
Summary: libnfsidmap sss.so plugin MUST always use fully qualified names
Status: ASSIGNED
Alias: None
Product: Red Hat Enterprise Linux 8
Classification: Red Hat
Component: sssd
Version: 8.0
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: pre-dev-freeze
: 8.1
Assignee: SSSD Maintainers
QA Contact: sssd-qe
URL:
Whiteboard:
Keywords:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2017-09-12 20:26 UTC by James Ralston
Modified: 2019-02-22 15:41 UTC (History)
11 users (show)

(edit)
Clone Of:
(edit)
Last Closed:
hartsjc: needinfo? (sssd-maint)


Attachments (Terms of Use)


External Trackers
Tracker ID Priority Status Summary Last Updated
Fedora Pagure SSSD/sssd issue 3535 None None None 2018-12-14 21:55 UTC

Description James Ralston 2017-09-12 20:26:01 UTC
TL;DR: omitting the @domain part of NFSv4 attribute values when use_fully_qualified_names is false is a clear violation of RFC7530, and breaks the ability of Linux NFSv4 RPCSEC_GSS clients to interoperate with non-Linux NFSv4 RPCSEC_GSS servers.

Description of problem:

Currently, when the libnfsidmap sss.so plugin maps uids/gids to NFSv4 attribute values (for use with the RPCSEC_GSS NFSv4 security mechanisms), it looks to the value of the use_fully_qualified_names sssd.conf configuration option to decide whether to generate fully-qualified user@domain attribute values, or whether to omit the "@domain" component of the attribute value.

We can clearly see this by looking at the result of what a RHEL7 NFSv4 client sends to server when calling chown on a nfsvers=4.1,sec=krb5 mount, when use_fully_qualified_names is set to "false" (the default) in sssd.conf for the example.org domain:

Network File System, Ops(4): SEQUENCE, PUTFH, SETATTR, GETATTR
    [Program Version: 4]
    [V4 Procedure: COMPOUND (1)]
    Tag: <EMPTY>
    minorversion: 1
    Operations (count: 4): SEQUENCE, PUTFH, SETATTR, GETATTR
        Opcode: SEQUENCE (53)
        Opcode: PUTFH (22)
        Opcode: SETATTR (34)
            stateid
            Attr mask: 0x00000010 (OWNER)
                reco_attr: OWNER (36)
                    fattr4_owner: testuser
                        length: 8
                        contents: testuser
        Opcode: GETATTR (9)
    [Main Opcode: SETATTR (34)]

Note that the fattr4_owner field is the bare username, with the @example.org domain.

If we set use_fully_qualified_names to "true" in sssd.conf for the example.org domain, the libnfsidmap sss.so plugin correctly qualifies the username with the @example.org domain:

Network File System, Ops(4): SEQUENCE, PUTFH, SETATTR, GETATTR
    [Program Version: 4]
    [V4 Procedure: COMPOUND (1)]
    Tag: <EMPTY>
    minorversion: 1
    Operations (count: 4): SEQUENCE, PUTFH, SETATTR, GETATTR
        Opcode: SEQUENCE (53)
        Opcode: PUTFH (22)
        Opcode: SETATTR (34)
            stateid
            Attr mask: 0x00000010 (OWNER)
                reco_attr: OWNER (36)
                    fattr4_owner: testuser@example.org
                        length: 20
                        contents: testuser@example.org
                        fill bytes: opaque data
        Opcode: GETATTR (9)
    [Main Opcode: SETATTR (34)]

The problem here is that the libnfsidmap sss.so plugin MUST NOT omit the @domain component when mapping uids/gids to NFSv4 attribute values, because this is clear violation of RFC7530.  RFC7530§5.9 states:

"In the case where there is no translation available to the client or server, the attribute value will be constructed without the "@". Therefore, the absence of the "@" from the owner or owner_group attribute signifies that no translation was available at the sender and that the receiver of the attribute should not use that string as a basis for translation into its own internal format. Even though the attribute value cannot be translated, it may still be useful. In the case of a client, the attribute string may be used for local display of ownership."

In other words, an attribute value without the "@domain" component does NOT mean "use the default domain for translation"; it explicitly means "reject this attribute".

We didn't see this breakage when we were using a RHEL7 NFSv4 RPCSEC_GSS client with a RHEL7 NFSv4 RPCSEC_GSS server, because on the server, the libnfsidmap sss.so plugin cheerfully interprets the bare username as being in the default domain, instead of obeying RFC7530 and rejecting it. So, because both RHEL7 NFSv4 RPCSEC_GSS clients and servers do the wrong thing, they can interoperate, because they do the wrong thing consistently.

But we are now attempting to use RHEL7 NFSv4 RPCSEC_GSS clients with a non-Linux NFSv4 RPCSEC_GSS server that properly obeys RFC7530 and rejects attribute values without the @domain component. Against such a compliant server, a RHEL7 NFSv4 RPCSEC_GSS client cannot manipulate any user/group attribute values. This is a complete showstopper for us.

(Setting use_fully_qualified_names = true isn't an acceptable workaround for us, as this would cause a huge, user-visible impact to our enterprise environment.)

The fix for this is to update the libnfsidmap sss.so plugin code to always map uids/gids to fully-qualified name@domain values, regardless of the value of the use_fully_qualified_names option in the sssd.conf file.

Unfortunately, for mapping names to uids/gids, I would argue that the libnfsidmap sss.so plugin code should continue to default unqualified names to the default domain, even though that is a violation of RFC7530, in order not to break older clients that haven't updated.

Version-Release number of selected component (if applicable):

sssd-common-1.15.2-50.el7

Comment 2 James Ralston 2017-09-12 20:57:34 UTC
Cross-filed as support case 01930265.

I filed this bug against RHEL7, but this affects all sssd-enabled distributions across the board (RHEL6, Fedora, et. al.).

Additionally, in case it wasn't clear, this bug also breaks the ability of non-Linux NFSv4 RPCSEC_GSS clients to interoperate with Linux NFSv4 RPCSEC_GSS servers.

Comment 3 James Ralston 2017-09-12 22:33:25 UTC
Furthermore, just in case it's not clear, we specifically reject the assertion in sss_rpcidmapd(5), that the solution is to always enable use_fully_qualified_names. That is inadequate because:

1. Not enabling use_fully_qualified_names will result in the Linux NFSv4 client/server implementation violating RFC7530. This is not something sssd should enable under ANY circumstances, let alone by default.

2. In the case where only one domain is in use—which is the case for many sites—enabling use_fully_qualified_names adds needless overhead and pain.

From looking at the code, I think the easiest way to implement this is to modify sss_nfs_uid_to_name() and sss_nfs_gid_to_name() to scan the result of id_to_name() that was placed into (char *name). If a "@" character is found, then take no action; otherwise, append "@" + the (char *domain) argument that was passed to the function (which is currently unused).

Unfortunately, this will trigger another bug: if the administrator has enabled multiple domains in sssd without enabling use_fully_qualified_names, and id_to_name() finds a match in a domain different than the one being used as the "Domain" setting in the [General] section of /etc/idmapd.conf, then appending the char *domain argument will produce an incorrect result.

But even if use_fully_qualified_names were enabled, and the result were qualified with the matching domain, that domain would be different than the idmapd domain, so it would be nonsensical to return it.

The real issue is that all mappings the libnfsidmap sss.so plugin performs should be constrained to the NFSv4 idmap domain, but there appears to be no way to constrain id_to_name() to return results only for a specific domain.

Is there a function equivalent to id_to_name() that additionally takes a specific domain to search?

Comment 4 Jakub Hrozek 2017-09-14 14:26:59 UTC
So far the only way to filter out certain by-ID requests from certain domains is to limit the ID ranges using the min_id/max_id parameters. Would that help your case?

At the moment, the by-ID requests cannot include a domain to search with in the wire protocol. It's something that could be added, potentially, but the current code doesn't allow it.

Comment 5 James Ralston 2017-09-14 19:10:01 UTC
(In reply to Jakub Hrozek from comment #4)

> So far the only way to filter out certain by-ID requests from certain
> domains is to limit the ID ranges using the min_id/max_id parameters. Would
> that help your case?

It's not necessary for us: while we have multiple domains, each of our Linux hosts has one (and only one) domain enabled in sssd.conf.

So, in our particular environment, modifying the libnfsidmap sss.so plugin to append @domain to the reply from the SSS_NSS_GETPWUID/SSS_NSS_GETPWUID calls (if the reply lacked a domain due to use_fully_qualified_names not being enabled) would result in correct behavior.

But that doesn't solve the general case.

> At the moment, the by-ID requests cannot include a domain to search with in
> the wire protocol. It's something that could be added, potentially, but the
> current code doesn't allow it.

Alas, I reached the same conclusion after studying the code in more detail.

The problem here is that the libnfsidmap sss.so plugin needs to perform extremely specific functions:

1. Translate a user@domain NFSv4 attribute to a uid, where the @domain part must match the Domain setting in /etc/idmapd.conf, and the translation must be restricted to that domain.

2. Translate a uid to a user@domain NFSv4 attribute, where the @domain part must match the Domain setting in /etc/idmapd.conf, and the translation must be restricted to that domain.

The libnfsidmap sss.so plugin can perform #1 easily, as passing the user@domain to sssd's SSS_NSS_GETPWNAM function will ensure that the lookup is constrained to the domain in question. (Although the plugin doesn't check to make sure the @domain portion matches the Domain setting in /etc/idmapd.conf.)

But #2 is currently intractable, because sssd's SSS_NSS_GETPWUID call doesn't provide a mechanism to constrain the search to a specific domain. So if the host in question has multiple domains configured in sssd, and those domains have overlapping uid/gid ranges, then the libnfsidmap sss.so plugin can return incorrect results.

Compounding this is the issue that if use_fully_qualified_names isn't enabled, sssd will omit the @domain part of the returned lookup.  But appending the NFSv4 idmapd domain isn't safe, because the resulting name can't be guaranteed to actually be from that domain.

So, in conclusion, this means that the current implementation of the libnfsidmap sss.so plugin is faced with two options, both unpalatable:

1. Return non-compliant NFSv4 attribute objects lacking the @domain part, which will cause Linux NFSv4 RPCSEC_GSS clients/servers to violate RFC7530 and thus break interoperability with non-Linux-based NFSv4 RPCSEC_GSS clients/servers.

2. Return compliant (but potentially wrong) user@domain NFSv4 attribute objects.

The libnfsidmap sss.so plugin currently implements option #1, and upon reflection, I think this is probably the best choice: better to fail (which is how RFC7530-compliant clients/servers will interpret an attribute lacking the @domain part) than to potentially return incorrect results.

(All of the above applies to group/gid translation as well.)

The only way to truly solve this problem is to extend the sssd API with calls that will constrain id-based lookups to a specific domain. E.g., sssd needs SSS_NSS_GETPWUID_IN_DOMAIN and SSS_NSS_GETGRGID_IN_DOMAIN (for example) calls.

We're grateful for the libnfsidmap sss.so plugin, because without it, Linux NFSv4 RPCSEC_GSS clients/servers in heterogeneous environments wouldn't even be possible. And the limitations of the libnfsidmap sss.so plugin aren't anyone's fault—the sssd API was cooked before the libnfsidmap sss.so plugin was created, so its needs couldn't have easily been foreseen.

But without extending the sssd API, the libnfsidmap sss.so plugin cannot behave correctly for Linux NFSv4 RPCSEC_GSS clients/servers in heterogeneous environments. That is an important goal, so I would argue it is worthwhile to extend the sssd API to enable it, even if that code doesn't land in RHEL until 7.5, 7.6, or even 8.0.

Comment 6 Sumit Bose 2017-09-15 07:11:19 UTC
Thank you for your detailed analysis.

JFYI, we are planning to offer and extended API for other use cases as well. I will started with a design page which should explain the new API. I'll try to cover the 'ID_IN_DOMAIN' case as well and will post a link here when the page is done. Feel free to ping me if this doesn't happen during the next 4 weeks.

bye,
Sumit

Comment 7 Jakub Hrozek 2017-10-04 19:32:28 UTC
So far I reproposed the bug to 7.6, because the 7.5 development is quite full already. I'll also clone the bug upstream so that we track it in pagure as well.

Comment 8 Jakub Hrozek 2017-10-04 19:32:35 UTC
Upstream ticket:
https://pagure.io/SSSD/sssd/issue/3535

Comment 9 Anthony Messina 2018-01-31 01:45:57 UTC
(In reply to Sumit Bose from comment #6)
> Thank you for your detailed analysis.
> 
> JFYI, we are planning to offer and extended API for other use cases as well.
> I will started with a design page which should explain the new API. I'll try
> to cover the 'ID_IN_DOMAIN' case as well and will post a link here when the
> page is done. Feel free to ping me if this doesn't happen during the next 4
> weeks.
> 
> bye,
> Sumit

Hi Sumit. Did you have a chance to create the design page to resolve this issue? I am checking for updates at https://docs.pagure.org/SSSD.sssd/design_pages/index.html


Note You need to log in before you can comment on or make changes to this bug.