Allow leading digits in userquota/groupquota names

While setting/getting userquota and groupquota properties, the input
was not treated as a possible username or groupname if it had a
leading digit. While useradd in linux recommends the regexp
[a-z_][a-z0-9_-]*[$]? , it is not enforced. This causes problem for
usernames with leading digits in them. We need to be able to support
getting and setting properties for this unconventional but possible
input category

I've updated the code to validate the username or groupname directly
via the API. Also, note that I moved this validation to the beginning
before the check for SID names with @. This also supports usernames
with @ character in them which are valid. Only when input with @ is
not a valid username, it is interpreted as a potential SID name.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #428
This commit is contained in:
Suman Chakravartula 2011-11-19 11:53:12 -08:00 committed by Brian Behlendorf
parent ca5fd24984
commit ada8ec1ec5
1 changed files with 28 additions and 39 deletions

View File

@ -2249,15 +2249,19 @@ out:
* convert the propname into parameters needed by kernel * convert the propname into parameters needed by kernel
* Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829 * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
* Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789 * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
* Eg: groupquota@staff -> ZFS_PROP_GROUPQUOTA, "", 1234
* Eg: groupused@staff -> ZFS_PROP_GROUPUSED, "", 1234
*/ */
static int static int
userquota_propname_decode(const char *propname, boolean_t zoned, userquota_propname_decode(const char *propname, boolean_t zoned,
zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp) zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
{ {
zfs_userquota_prop_t type; zfs_userquota_prop_t type;
char *cp, *end; char *cp;
char *numericsid = NULL;
boolean_t isuser; boolean_t isuser;
boolean_t isgroup;
struct passwd *pw;
struct group *gr;
domain[0] = '\0'; domain[0] = '\0';
@ -2271,18 +2275,29 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
return (EINVAL); return (EINVAL);
*typep = type; *typep = type;
isuser = (type == ZFS_PROP_USERQUOTA || isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED);
type == ZFS_PROP_USERUSED); isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED);
cp = strchr(propname, '@') + 1; cp = strchr(propname, '@') + 1;
if (strchr(cp, '@')) { if (isuser && (pw = getpwnam(cp)) != NULL) {
if (zoned && getzoneid() == GLOBAL_ZONEID)
return (ENOENT);
*ridp = pw->pw_uid;
} else if (isgroup && (gr = getgrnam(cp)) != NULL) {
if (zoned && getzoneid() == GLOBAL_ZONEID)
return (ENOENT);
*ridp = gr->gr_gid;
} else if (strchr(cp, '@')) {
#ifdef HAVE_IDMAP #ifdef HAVE_IDMAP
/* /*
* It's a SID name (eg "user@domain") that needs to be * It's a SID name (eg "user@domain") that needs to be
* turned into S-1-domainID-RID. * turned into S-1-domainID-RID.
*/ */
directory_error_t e; directory_error_t e;
char *numericsid = NULL;
char *end;
if (zoned && getzoneid() == GLOBAL_ZONEID) if (zoned && getzoneid() == GLOBAL_ZONEID)
return (ENOENT); return (ENOENT);
if (isuser) { if (isuser) {
@ -2299,14 +2314,6 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
if (numericsid == NULL) if (numericsid == NULL)
return (ENOENT); return (ENOENT);
cp = numericsid; cp = numericsid;
/* will be further decoded below */
#else
return (ENOSYS);
#endif /* HAVE_IDMAP */
}
if (strncmp(cp, "S-1-", 4) == 0) {
/* It's a numeric SID (eg "S-1-234-567-89") */
(void) strlcpy(domain, cp, domainlen); (void) strlcpy(domain, cp, domainlen);
cp = strrchr(domain, '-'); cp = strrchr(domain, '-');
*cp = '\0'; *cp = '\0';
@ -2314,39 +2321,22 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
errno = 0; errno = 0;
*ridp = strtoull(cp, &end, 10); *ridp = strtoull(cp, &end, 10);
if (numericsid) { free(numericsid);
free(numericsid);
numericsid = NULL;
}
if (errno != 0 || *end != '\0') if (errno != 0 || *end != '\0')
return (EINVAL); return (EINVAL);
} else if (!isdigit(*cp)) { #else
/* return (ENOSYS);
* It's a user/group name (eg "user") that needs to be #endif /* HAVE_IDMAP */
* turned into a uid/gid
*/
if (zoned && getzoneid() == GLOBAL_ZONEID)
return (ENOENT);
if (isuser) {
struct passwd *pw;
pw = getpwnam(cp);
if (pw == NULL)
return (ENOENT);
*ridp = pw->pw_uid;
} else {
struct group *gr;
gr = getgrnam(cp);
if (gr == NULL)
return (ENOENT);
*ridp = gr->gr_gid;
}
} else { } else {
#ifdef HAVE_IDMAP #ifdef HAVE_IDMAP
/* It's a user/group ID (eg "12345"). */ /* It's a user/group ID (eg "12345"). */
uid_t id = strtoul(cp, &end, 10); uid_t id;
idmap_rid_t rid; idmap_rid_t rid;
char *mapdomain; char *mapdomain;
char *end;
id = strtoul(cp, &end, 10);
if (*end != '\0') if (*end != '\0')
return (EINVAL); return (EINVAL);
if (id > MAXUID) { if (id > MAXUID) {
@ -2364,7 +2354,6 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
#endif /* HAVE_IDMAP */ #endif /* HAVE_IDMAP */
} }
ASSERT3P(numericsid, ==, NULL);
return (0); return (0);
} }