diff -up libnfsidmap-0.25/idmapd.conf.5.orig libnfsidmap-0.25/idmapd.conf.5 --- libnfsidmap-0.25/idmapd.conf.5.orig 2017-01-10 13:30:28.696901000 -0500 +++ libnfsidmap-0.25/idmapd.conf.5 2017-01-10 13:32:44.241316000 -0500 @@ -63,6 +63,30 @@ The local NFSv4 domain name. An NFSv4 d a unique username<->UID and groupname<->GID mapping. (Default: Host's fully-qualified DNS domain name) .TP +.B No-Strip +In multi-domain environments, some NFS servers will append the identity +management domain to the owner and owner_group in lieu of a true NFSv4 +domain. This option can facilitate lookups in such environments. If +set to a value other than "none", the nsswitch plugin will first pass +the name to the password/group lookup function without stripping the +domain off. If that mapping fails then the plugin will try again using +the old method (comparing the domain in the string to the Domain value, +stripping it if it matches, and passing the resulting short name to the +lookup function). Valid values are "user", "group", "both", and +"none". +(Default: "none") +.TP +.B Reformat-Group +Winbind has a quirk whereby doing a group lookup in UPN format +(e.g. staff@americas.example.com) will cause the group to be +displayed prefixed with the full domain in uppercase +(e.g. AMERICAS.EXAMPLE.COM\\staff) instead of in the familiar netbios +name format (e.g. AMERICAS\\staff). Setting this option to true +causes the name to be reformatted before passing it to the group +lookup function in order to work around this. This setting is +ignored unless No-Strip is set to either "both" or "group". +(Default: "false") +.TP .B Local-Realms A comma-separated list of Kerberos realm names that may be considered equivalent to the local realm name. For example, users juser@ORDER.EDU and juser@MAIL.ORDER.EDU diff -up libnfsidmap-0.25/idmapd.conf.orig libnfsidmap-0.25/idmapd.conf --- libnfsidmap-0.25/idmapd.conf.orig 2011-12-05 15:28:10.000000000 -0500 +++ libnfsidmap-0.25/idmapd.conf 2017-01-10 13:32:44.235315000 -0500 @@ -4,6 +4,29 @@ # The default is the host's DNS domain name. #Domain = local.domain.edu +# In multi-domain environments, some NFS servers will append the identity +# management domain to the owner and owner_group in lieu of a true NFSv4 +# domain. This option can facilitate lookups in such environments. If +# set to a value other than "none", the nsswitch plugin will first pass +# the name to the password/group lookup function without stripping the +# domain off. If that mapping fails then the plugin will try again using +# the old method (comparing the domain in the string to the Domain value, +# stripping it if it matches, and passing the resulting short name to the +# lookup function). Valid values are "user", "group", "both", and +# "none". The default is "none". +#No-Strip = none + +# Winbind has a quirk whereby doing a group lookup in UPN format +# (e.g. staff@americas.example.com) will cause the group to be +# displayed prefixed with the full domain in uppercase +# (e.g. AMERICAS.EXAMPLE.COM\staff) instead of in the familiar netbios +# name format (e.g. AMERICAS\staff). Setting this option to true +# causes the name to be reformatted before passing it to the group +# lookup function in order to work around this. This setting is +# ignored unless No-Strip is set to either "both" or "group". +# The default is "false". +#Reformat-Group = false + # The following is a comma-separated list of Kerberos realm # names that should be considered to be equivalent to the # local realm, such that @REALM.A can be assumed to diff -up libnfsidmap-0.25/libnfsidmap.c.orig libnfsidmap-0.25/libnfsidmap.c --- libnfsidmap-0.25/libnfsidmap.c.orig 2017-01-10 13:30:28.837901000 -0500 +++ libnfsidmap-0.25/libnfsidmap.c 2017-01-10 13:32:44.247315000 -0500 @@ -60,6 +60,8 @@ static char *default_domain; static struct conf_list *local_realms; int idmap_verbosity = 0; +int no_strip = 0; +int reformat_group = 0; static struct mapping_plugin **nfs4_plugins = NULL; static struct mapping_plugin **gss_plugins = NULL; uid_t nobody_uid = (uid_t)-1; @@ -234,6 +236,8 @@ int nfs4_init_name_mapping(char *conffil int dflt = 0; struct conf_list *nfs4_methods, *gss_methods; char *nobody_user, *nobody_group; + char *nostrip; + char *reformatgroup; /* XXX: need to be able to reload configurations... */ if (nfs4_plugins) /* already succesfully initialized */ @@ -306,6 +310,26 @@ int nfs4_init_name_mapping(char *conffil IDMAP_LOG(1, ("libnfsidmap: Realms list: ")); } + nostrip = conf_get_str_with_def("General", "No-Strip", "none"); + if (strcasecmp(nostrip, "both") == 0) + no_strip = IDTYPE_USER|IDTYPE_GROUP; + else if (strcasecmp(nostrip, "group") == 0) + no_strip = IDTYPE_GROUP; + else if (strcasecmp(nostrip, "user") == 0) + no_strip = IDTYPE_USER; + else + no_strip = 0; + + if (no_strip & IDTYPE_GROUP) { + reformatgroup = conf_get_str_with_def("General", "Reformat-Group", "false"); + if ((strcasecmp(reformatgroup, "true") == 0) || + (strcasecmp(reformatgroup, "on") == 0) || + (strcasecmp(reformatgroup, "yes") == 0)) + reformat_group = 1; + else + reformat_group = 0; + } + nfs4_methods = conf_get_list("Translation", "Method"); if (nfs4_methods) { IDMAP_LOG(1, ("libnfsidmap: processing 'Method' list")); diff -up libnfsidmap-0.25/nfsidmap_internal.h.orig libnfsidmap-0.25/nfsidmap_internal.h --- libnfsidmap-0.25/nfsidmap_internal.h.orig 2011-12-05 15:28:10.000000000 -0500 +++ libnfsidmap-0.25/nfsidmap_internal.h 2017-01-10 13:32:44.253315000 -0500 @@ -63,6 +63,8 @@ typedef enum { IDTYPE_GROUP = 2 } idtypes; +extern int no_strip; +extern int reformat_group; extern int idmap_verbosity; extern nfs4_idmap_log_function_t idmap_log_func; /* Level zero always prints, others print depending on verbosity level */ diff -up libnfsidmap-0.25/nss.c.orig libnfsidmap-0.25/nss.c --- libnfsidmap-0.25/nss.c.orig 2017-01-10 13:30:28.892903000 -0500 +++ libnfsidmap-0.25/nss.c 2017-01-10 13:32:44.259316000 -0500 @@ -45,6 +45,7 @@ #include #include #include +#include #include "nfsidmap.h" #include "nfsidmap_internal.h" #include "cfg.h" @@ -58,14 +59,20 @@ * and ignore the domain entirely when looking up a name. */ -static int write_name(char *dest, char *localname, char *domain, size_t len) +static int write_name(char *dest, char *localname, char *domain, size_t len, + int doappend) { - if (strlen(localname) + 1 + strlen(domain) + 1 > len) { - return -ENOMEM; /* XXX: Is there an -ETOOLONG? */ + if (doappend || !strchr(localname,'@')) { + if (strlen(localname) + 1 + strlen(domain) + 1 > len) + return -ENOMEM; /* XXX: Is there an -ETOOLONG? */ + strcpy(dest, localname); + strcat(dest, "@"); + strcat(dest, domain); + } else { + if (strlen(localname) + 1 > len) + return -ENOMEM; + strcpy(dest, localname); } - strcpy(dest, localname); - strcat(dest, "@"); - strcat(dest, domain); return 0; } @@ -87,7 +94,10 @@ static int nss_uid_to_name(uid_t uid, ch err = -ENOENT; if (err) goto out_buf; - err = write_name(name, pw->pw_name, domain, len); + if (no_strip & IDTYPE_USER) + err = write_name(name, pw->pw_name, domain, len, 0); + else + err = write_name(name, pw->pw_name, domain, len, 1); out_buf: free(buf); out: @@ -121,7 +131,10 @@ static int nss_gid_to_name(gid_t gid, ch if (err) goto out_buf; - err = write_name(name, gr->gr_name, domain, len); + if (no_strip & IDTYPE_GROUP) + err = write_name(name, gr->gr_name, domain, len, 0); + else + err = write_name(name, gr->gr_name, domain, len, 1); out_buf: free(buf); out: @@ -164,7 +177,8 @@ struct pwbuf { char buf[1]; }; -static struct passwd *nss_getpwnam(const char *name, const char *domain, int *err_p) +static struct passwd *nss_getpwnam(const char *name, const char *domain, + int *err_p, int dostrip) { struct passwd *pw; struct pwbuf *buf; @@ -180,22 +194,29 @@ static struct passwd *nss_getpwnam(const goto err; err = EINVAL; - localname = strip_domain(name, domain); - IDMAP_LOG(4, ("nss_getpwnam: name '%s' domain '%s': " - "resulting localname '%s'", name, domain, localname)); - if (localname == NULL) { - IDMAP_LOG(0, ("nss_getpwnam: name '%s' does not map " - "into domain '%s'", name, - domain ? domain : "")); - goto err_free_buf; - } + if (dostrip) { + localname = strip_domain(name, domain); + IDMAP_LOG(4, ("nss_getpwnam: name '%s' domain '%s': " + "resulting localname '%s'", name, domain, localname)); + if (localname == NULL) { + IDMAP_LOG(0, ("nss_getpwnam: name '%s' does not map " + "into domain '%s'", name, + domain ? domain : "")); + goto err_free_buf; + } - err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw); - if (pw == NULL && domain != NULL) - IDMAP_LOG(0, - ("nss_getpwnam: name '%s' not found in domain '%s'", - localname, domain)); - free(localname); + err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw); + if (pw == NULL && domain != NULL) + IDMAP_LOG(1, + ("nss_getpwnam: name '%s' not found in domain '%s'", + localname, domain)); + free(localname); + } else { + err = getpwnam_r(name, &buf->pwbuf, buf->buf, buflen, &pw); + if (pw == NULL) + IDMAP_LOG(1, + ("nss_getpwnam: name '%s' not found (domain not stripped)", name)); + } if (err == 0 && pw != NULL) { *err_p = 0; return pw; @@ -217,28 +238,83 @@ static int nss_name_to_uid(char *name, u int err = -ENOENT; domain = get_default_domain(); - pw = nss_getpwnam(name, domain, &err); + if (no_strip & IDTYPE_USER) { + pw = nss_getpwnam(name, domain, &err, 0); + if (pw != NULL) + goto out_uid; + } + pw = nss_getpwnam(name, domain, &err, 1); if (pw == NULL) goto out; +out_uid: *uid = pw->pw_uid; + IDMAP_LOG(4, ("nss_name_to_uid: name '%s' uid %u", name, *uid)); free(pw); err = 0; out: return err; } -static int nss_name_to_gid(char *name, gid_t *gid) +static char *reformat_name(const char *name) +{ + const char *domain; + const char *c; + const char *d; + char *l = NULL; + int len; + int dlen = 0; + int i; + + c = strchr(name, '@'); + if (c == NULL) + goto out; + len = c - name; + domain = ++c; + d = strchr(domain, '.'); + if (d == NULL) + goto out; + dlen = d - domain; + l = malloc(dlen + 1 + len + 1); + if (l == NULL) + goto out; + for (i = 0; i < dlen; i++) + l[i] = toupper(domain[i]); + l[dlen] = '\\'; + memcpy(l + dlen + 1, name, len); + l[dlen + 1 + len] = '\0'; +out: + return l; +} + +static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip) { struct group *gr = NULL; struct group grbuf; - char *buf, *localname, *domain; + char *buf, *domain; size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); int err = -EINVAL; + char *localname = NULL; + char *ref_name = NULL; domain = get_default_domain(); - localname = strip_domain(name, domain); - if (!localname) - goto out; + if (dostrip) { + localname = strip_domain(name, domain); + IDMAP_LOG(4, ("nss_name_to_gid: name '%s' domain '%s': " + "resulting localname '%s'", name, domain, localname)); + if (!localname) { + IDMAP_LOG(0, ("nss_name_to_gid: name '%s' does not map " + "into domain '%s'", name, domain)); + goto out; + } + } else if (reformat_group) { + ref_name = reformat_name(name); + if (ref_name == NULL) { + IDMAP_LOG(1, ("nss_name_to_gid: failed to reformat name '%s'", + name)); + err = -ENOENT; + goto out; + } + } err = -ENOMEM; if (buflen > UINT_MAX) @@ -248,9 +324,24 @@ static int nss_name_to_gid(char *name, g buf = malloc(buflen); if (!buf) goto out_name; - err = -getgrnam_r(localname, &grbuf, buf, buflen, &gr); - if (gr == NULL && !err) + if (dostrip) + err = -getgrnam_r(localname, &grbuf, buf, buflen, &gr); + else if (reformat_group) + err = -getgrnam_r(ref_name, &grbuf, buf, buflen, &gr); + else + err = -getgrnam_r(name, &grbuf, buf, buflen, &gr); + if (gr == NULL && !err) { + if (dostrip) + IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found " + "in domain '%s'", localname, domain)); + else if (reformat_group) + IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found " + "(reformatted)", ref_name)); + else + IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found " + "(domain not stripped)", name)); err = -ENOENT; + } if (err == -ERANGE) { buflen *= 2; free(buf); @@ -260,10 +351,28 @@ static int nss_name_to_gid(char *name, g if (err) goto out_buf; *gid = gr->gr_gid; + IDMAP_LOG(4, ("nss_name_to_gid: name '%s' gid %u", name, *gid)); out_buf: free(buf); out_name: - free(localname); + if (dostrip) + free(localname); + if (reformat_group) + free(ref_name); +out: + return err; +} + +static int nss_name_to_gid(char *name, gid_t *gid) +{ + int err = 0; + + if (no_strip & IDTYPE_GROUP) { + err = _nss_name_to_gid(name, gid, 0); + if (!err) + goto out; + } + err = _nss_name_to_gid(name, gid, 1); out: return err; } @@ -306,7 +415,7 @@ static int nss_gss_princ_to_ids(char *se return -ENOENT; } /* XXX: this should call something like getgssauthnam instead? */ - pw = nss_getpwnam(princ, NULL, &err); + pw = nss_getpwnam(princ, NULL, &err, 0); if (pw == NULL) { err = -ENOENT; goto out; @@ -329,7 +438,7 @@ int nss_gss_princ_to_grouplist(char *sec goto out; /* XXX: not quite right? Need to know default realm? */ /* XXX: this should call something like getgssauthnam instead? */ - pw = nss_getpwnam(princ, NULL, &ret); + pw = nss_getpwnam(princ, NULL, &ret, 0); if (pw == NULL) { ret = -ENOENT; goto out;