I've set up a Kerberos domain (the KDC is on a pfSense box) and an NFSv4 server which is on a Synology NAS. I'm mounting the remote share on my FreeBSD 14.2-STABLE client with an entry in fstab like this: nas:/volume1/homes /shared nfs rw,nfsv4,gssname=host,sec=krb5p,pnfs 0 0 I have gssd and nfsuserd running, but when I list the contents of the directory, I get: $ ls -l /shared total 0 drwxr-xr-x 1 nobody nogroup 0 Sep 27 21:11 admin drwxr-xr-x 1 nobody nogroup 0 Nov 5 19:58 jmmv drwxr-xr-x 1 nobody nogroup 0 Oct 8 16:59 manager The "jmmv" directory should show up as owned by "jmmv", but it shows up as "nobody". When I start nfsuserd with -verbose set, I get: nfsuserd: domain=meroh.net usermax=200 usertimeout=60 The domain meroh.net is the correct domain for the machine, and the Kerberos domain name is MEROH.NET. I've added logging statements to nfsuserd and can see that the GETUSER requests from the kernel carry jmmv@MEROH.NET in them, which seems good, but still things don't work. In playing with the code, I modified the nfsuserd code with this: ------ --- a/usr.sbin/nfsuserd/nfsuserd.c +++ b/usr.sbin/nfsuserd/nfsuserd.c @@ -377,8 +377,15 @@ main(int argc, char *argv[]) setgrent(); while (i < nid.nid_usermax && (grp = getgrent())) { nid.nid_gid = grp->gr_gid; - nid.nid_name = grp->gr_name; - nid.nid_namelen = strlen(grp->gr_name); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s@%s", grp->gr_name, dnsname); + char *ptr = strchr(buf, '@'); + while (*ptr != '\0') { + *ptr = toupper(*ptr); + ptr++; + } + nid.nid_name = buf; + nid.nid_namelen = strlen(nid.nid_name); nid.nid_ngroup = 0; nid.nid_grps = NULL; nid.nid_flag = NFSID_ADDGID; @@ -416,8 +423,15 @@ main(int argc, char *argv[]) continue; check_dups[i - start_uidpos] = pwd->pw_uid; nid.nid_uid = pwd->pw_uid; - nid.nid_name = pwd->pw_name; - nid.nid_namelen = strlen(pwd->pw_name); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s@%s", pwd->pw_name, dnsname); + char *ptr = strchr(buf, '@'); + while (*ptr != '\0') { + *ptr = toupper(*ptr); + ptr++; + } + nid.nid_name = buf; + nid.nid_namelen = strlen(nid.nid_name); if (manage_gids != 0) { /* Get the group list for this user. */ ngroup = NGROUPS; ------ And got the usernames to work properly _after_ mount. This is insufficient when the cache expires though because the above is only for the daemon initialization, but seems to show that there is some inconsistency between what nfsuserd thinks the domain should be expressed and what the kernel expects. Note that, in the above, the toupper is important as well. I do not know yet if this is a failure of my configuration or a bug in nfsuserd / the kernel. Could you assist / confirm?
Created attachment 255020 [details] Sample patch I don't yet know what the actual root cause is, but this patch hides the issue for me.
The original NFSv4 RFCs did not specify how a "user domain" was to be compared. By happenstance, I only found out yesterday (at the IETF working group meeting) that the spec. now does allow/require that the domain spec be compared like a DNS one (ie. case independent). This will need to be patched in the kernel, but I won't get to it for a few weeks. You could try: # nfsuserd -domain MECOH.NET or setting it as MECOH.NET via nfsuserd_flags="-domain MECOH.NET" in /etc/rc.conf. This should work around the problem. (Or change the server's config to use a lower case domain name.) I will come up with a patch for -current in a few weeks.
I had already tried "-domain MEROH.NET" before, but... that doesn't work: nfsuserd internally converts the domain to lowercase. Which means: I had also tried to disable the lowercase conversion and it *still* doesn't work. Then, I found code in the kernel that attempts to do some weird case comparison, but only if the domain is not fully specified as uppercase. Or something like that... but I stopped investigating. I wasn't convinced that *just* the case match is the issue here. I don't know what the expectations are and how the code should be to support them. So... I'll look forward to your patch; thanks for looking!
Hmm, yes. Mixed case and all upper case are not the same. THe nfsuserd code that maps it to lower case would have been there to "avoid" the case issue. I will have to look at the RFCs to try and decide if all upper case is supposed to match lower/mixed case. I believe most reserve all upper case for KERBEROS Realms. Long ago (like 20years ago when this was first spec'd), some wanted the "domain" to be a Kerberos Realm, but others rejected that, since other mechanisms than Kerberos were planned for the GSSAPI. (Those basically have never happened, although there is a recent discussion w.r.t. Oauth2, or whatever it is called.) There was also a plan to make the "domain" a new DNS record type, but that never happened, either. But, yes, the problem is just getting the "domain" to match as same.
RFC8881 (the most recent for NFSv4.1) defines the domain component (what is after the @) as a DNS domain name. It does not specify it further. Since a DNS domain name is not case sensitive, the comparison should be case insensitive in the kernel. I believe the code was implemented the way it currently is because, long ago, upper case was discouraged and considered reserved for Kerberos Realms. I will come up with a patch. To work around the problem, you would need to re-configure the server, so that MECOH.NET becomes mecoh.net and then I think the problem would be resolved.
Created attachment 255038 [details] Allow the NFSv4 user domain match an all upper case domain name The code that compares the domain field for NFSv4 Owner/Owner_group attributes did not compare an all upper case domain as the same as a lower case domain. I believe this was done long ago, to discourage use of Kerberos Realms as domain names. The current RFC8881 simply specifies the domain component as a DNS domain name. As such, I believe the comparison should be case insensitive. This patch changes nfsrv_cmpmixedcase() so that the comparison is case insensitive. I cannot actually test this, since FreeBSD servers always present the domain name as lower case. Hopefully the reporter can test this patch. I do not believe any patch of nfsuserd.c will be needed once this patch is applied.
Thanks for the patch. I've applied it to 14.2-STABLE (with a merge conflict, but only in the comments), undone my nfsuserd patches, rebooted, and the name mapping seems to be working!
Ok. Thanks for testing the patch. I'll commit it to main in a couple of weeks, although I'll probably just replace nfsrv_cmpmixedcase() with strncasecmp().
A commit in branch main references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=0347ddf41f4226c0351d2d1d78f09e8300ebac93 commit 0347ddf41f4226c0351d2d1d78f09e8300ebac93 Author: Rick Macklem <rmacklem@FreeBSD.org> AuthorDate: 2024-11-24 20:47:56 +0000 Commit: Rick Macklem <rmacklem@FreeBSD.org> CommitDate: 2024-11-24 20:47:56 +0000 nfs_commonsubs.c: Make all upper case user domain work Without this patch, an all upper case user domain name (as specified by nfsuserd(8)) would not work. I believe this was done so that Kerberos realms were not confused with user domains. Now, RFC8881 specifies that the user domain name is a DNS name. As such, all upper case names should work. This patch fixes this case so that it works. The custom comparison function is no longer needed. PR: 282620 Tested by: jmmv MFC after: 2 weeks sys/fs/nfs/nfs_commonsubs.c | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-)
A commit in branch stable/14 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=7a4f370a352f89f00411a0e9cd7a7b8f8424be3a commit 7a4f370a352f89f00411a0e9cd7a7b8f8424be3a Author: Rick Macklem <rmacklem@FreeBSD.org> AuthorDate: 2024-11-24 20:47:56 +0000 Commit: Rick Macklem <rmacklem@FreeBSD.org> CommitDate: 2024-12-08 01:41:52 +0000 nfs_commonsubs.c: Make all upper case user domain work Without this patch, an all upper case user domain name (as specified by nfsuserd(8)) would not work. I believe this was done so that Kerberos realms were not confused with user domains. Now, RFC8881 specifies that the user domain name is a DNS name. As such, all upper case names should work. This patch fixes this case so that it works. The custom comparison function is no longer needed. PR: 282620 (cherry picked from commit 0347ddf41f4226c0351d2d1d78f09e8300ebac93) sys/fs/nfs/nfs_commonsubs.c | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-)
A commit in branch stable/13 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=f6245e432208cf1dd43f31b759db64aac7590108 commit f6245e432208cf1dd43f31b759db64aac7590108 Author: Rick Macklem <rmacklem@FreeBSD.org> AuthorDate: 2024-11-24 20:47:56 +0000 Commit: Rick Macklem <rmacklem@FreeBSD.org> CommitDate: 2024-12-08 01:49:55 +0000 nfs_commonsubs.c: Make all upper case user domain work Without this patch, an all upper case user domain name (as specified by nfsuserd(8)) would not work. I believe this was done so that Kerberos realms were not confused with user domains. Now, RFC8881 specifies that the user domain name is a DNS name. As such, all upper case names should work. This patch fixes this case so that it works. The custom comparison function is no longer needed. PR: 282620 (cherry picked from commit 0347ddf41f4226c0351d2d1d78f09e8300ebac93) sys/fs/nfs/nfs_commonsubs.c | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-)
Patch has been committed and MFC'd.