Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab8e4aee authored by Eric W. Biederman's avatar Eric W. Biederman
Browse files

nfsd: Handle kuids and kgids in the nfs4acl to posix_acl conversion



In struct nfs4_ace remove the member who and replace it with an
anonymous union holding who_uid and who_gid.  Allowing typesafe
storage uids and gids.

Add a helper pace_gt for sorting posix_acl_entries.

In struct posix_user_ace_state to replace uid with a union
of kuid_t uid and kgid_t gid.

Remove all initializations of the deprecated posic_acl_entry
e_id field.  Which is not present when user namespaces are enabled.

Split find_uid into two functions find_uid and find_gid that work
in a typesafe manner.

In nfs4xdr update nfsd4_encode_fattr to deal with the changes
in struct nfs4_ace.

Rewrite nfsd4_encode_name to take a kuid_t and a kgid_t instead
of a generic id and flag if it is a group or a uid.  Replace
the group flag with a test for a valid gid.

Modify nfsd4_encode_user to take a kuid_t and call the modifed
nfsd4_encode_name.

Modify nfsd4_encode_group to take a kgid_t and call the modified
nfsd4_encode_name.

Modify nfsd4_encode_aclname to take an ace instead of taking the
fields of an ace broken out.  This allows it to detect if the ace is
for a user or a group and to pass the appropriate value while still
being typesafe.

Cc: "J. Bruce Fields" <bfields@fieldses.org>
Cc: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
parent 7c19723e
Loading
Loading
Loading
Loading
+46 −17
Original line number Diff line number Diff line
@@ -264,7 +264,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
			ace->flag = eflag;
			ace->access_mask = deny_mask_from_posix(deny, flags);
			ace->whotype = NFS4_ACL_WHO_NAMED;
			ace->who = pa->e_id;
			ace->who_uid = pa->e_uid;
			ace++;
			acl->naces++;
		}
@@ -273,7 +273,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
		ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
						   flags);
		ace->whotype = NFS4_ACL_WHO_NAMED;
		ace->who = pa->e_id;
		ace->who_uid = pa->e_uid;
		ace++;
		acl->naces++;
		pa++;
@@ -300,7 +300,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
		ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
						   flags);
		ace->whotype = NFS4_ACL_WHO_NAMED;
		ace->who = pa->e_id;
		ace->who_gid = pa->e_gid;
		ace++;
		acl->naces++;
		pa++;
@@ -329,7 +329,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
			ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
			ace->access_mask = deny_mask_from_posix(deny, flags);
			ace->whotype = NFS4_ACL_WHO_NAMED;
			ace->who = pa->e_id;
			ace->who_gid = pa->e_gid;
			ace++;
			acl->naces++;
		}
@@ -345,6 +345,18 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
	acl->naces++;
}

static bool
pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2)
{
	if (pace1->e_tag != pace2->e_tag)
		return pace1->e_tag > pace2->e_tag;
	if (pace1->e_tag == ACL_USER)
		return uid_gt(pace1->e_uid, pace2->e_uid);
	if (pace1->e_tag == ACL_GROUP)
		return gid_gt(pace1->e_gid, pace2->e_gid);
	return false;
}

static void
sort_pacl_range(struct posix_acl *pacl, int start, int end) {
	int sorted = 0, i;
@@ -355,8 +367,8 @@ sort_pacl_range(struct posix_acl *pacl, int start, int end) {
	while (!sorted) {
		sorted = 1;
		for (i = start; i < end; i++) {
			if (pacl->a_entries[i].e_id
					> pacl->a_entries[i+1].e_id) {
			if (pace_gt(&pacl->a_entries[i],
				    &pacl->a_entries[i+1])) {
				sorted = 0;
				tmp = pacl->a_entries[i];
				pacl->a_entries[i] = pacl->a_entries[i+1];
@@ -398,7 +410,10 @@ struct posix_ace_state {
};

struct posix_user_ace_state {
	uid_t uid;
	union {
		kuid_t uid;
		kgid_t gid;
	};
	struct posix_ace_state perms;
};

@@ -521,7 +536,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
	if (error)
		goto out_err;
	low_mode_from_nfs4(state->owner.allow, &pace->e_perm, flags);
	pace->e_id = ACL_UNDEFINED_ID;

	for (i=0; i < state->users->n; i++) {
		pace++;
@@ -531,7 +545,7 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
			goto out_err;
		low_mode_from_nfs4(state->users->aces[i].perms.allow,
					&pace->e_perm, flags);
		pace->e_id = state->users->aces[i].uid;
		pace->e_uid = state->users->aces[i].uid;
		add_to_mask(state, &state->users->aces[i].perms);
	}

@@ -541,7 +555,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
	if (error)
		goto out_err;
	low_mode_from_nfs4(state->group.allow, &pace->e_perm, flags);
	pace->e_id = ACL_UNDEFINED_ID;
	add_to_mask(state, &state->group);

	for (i=0; i < state->groups->n; i++) {
@@ -552,14 +565,13 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
			goto out_err;
		low_mode_from_nfs4(state->groups->aces[i].perms.allow,
					&pace->e_perm, flags);
		pace->e_id = state->groups->aces[i].uid;
		pace->e_gid = state->groups->aces[i].gid;
		add_to_mask(state, &state->groups->aces[i].perms);
	}

	pace++;
	pace->e_tag = ACL_MASK;
	low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags);
	pace->e_id = ACL_UNDEFINED_ID;

	pace++;
	pace->e_tag = ACL_OTHER;
@@ -567,7 +579,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
	if (error)
		goto out_err;
	low_mode_from_nfs4(state->other.allow, &pace->e_perm, flags);
	pace->e_id = ACL_UNDEFINED_ID;

	return pacl;
out_err:
@@ -587,12 +598,13 @@ static inline void deny_bits(struct posix_ace_state *astate, u32 mask)
	astate->deny |= mask & ~astate->allow;
}

static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array *a, uid_t uid)
static int find_uid(struct posix_acl_state *state, kuid_t uid)
{
	struct posix_ace_state_array *a = state->users;
	int i;

	for (i = 0; i < a->n; i++)
		if (a->aces[i].uid == uid)
		if (uid_eq(a->aces[i].uid, uid))
			return i;
	/* Not found: */
	a->n++;
@@ -603,6 +615,23 @@ static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array
	return i;
}

static int find_gid(struct posix_acl_state *state, kgid_t gid)
{
	struct posix_ace_state_array *a = state->groups;
	int i;

	for (i = 0; i < a->n; i++)
		if (gid_eq(a->aces[i].gid, gid))
			return i;
	/* Not found: */
	a->n++;
	a->aces[i].gid = gid;
	a->aces[i].perms.allow = state->everyone.allow;
	a->aces[i].perms.deny  = state->everyone.deny;

	return i;
}

static void deny_bits_array(struct posix_ace_state_array *a, u32 mask)
{
	int i;
@@ -636,7 +665,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
		}
		break;
	case ACL_USER:
		i = find_uid(state, state->users, ace->who);
		i = find_uid(state, ace->who_uid);
		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
			allow_bits(&state->users->aces[i].perms, mask);
		} else {
@@ -658,7 +687,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
		}
		break;
	case ACL_GROUP:
		i = find_uid(state, state->groups, ace->who);
		i = find_gid(state, ace->who_gid);
		if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
			allow_bits(&state->groups->aces[i].perms, mask);
		} else {
+25 −16
Original line number Diff line number Diff line
@@ -293,13 +293,13 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
			ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
			status = nfs_ok;
			if (ace->whotype != NFS4_ACL_WHO_NAMED)
				ace->who = 0;
				;
			else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
				status = nfsd_map_name_to_gid(argp->rqstp,
						buf, dummy32, &ace->who);
						buf, dummy32, &ace->who_gid);
			else
				status = nfsd_map_name_to_uid(argp->rqstp,
						buf, dummy32, &ace->who);
						buf, dummy32, &ace->who_uid);
			if (status)
				return status;
		}
@@ -1926,7 +1926,7 @@ static u32 nfs4_file_type(umode_t mode)
}

static __be32
nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, kuid_t uid, kgid_t gid,
			__be32 **p, int *buflen)
{
	int status;
@@ -1935,10 +1935,10 @@ nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
		return nfserr_resource;
	if (whotype != NFS4_ACL_WHO_NAMED)
		status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
	else if (group)
		status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
	else if (gid_valid(gid))
		status = nfsd_map_gid_to_name(rqstp, gid, (u8 *)(*p + 1));
	else
		status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
		status = nfsd_map_uid_to_name(rqstp, uid, (u8 *)(*p + 1));
	if (status < 0)
		return nfserrno(status);
	*p = xdr_encode_opaque(*p, NULL, status);
@@ -1948,22 +1948,33 @@ nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
}

static inline __be32
nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **p, int *buflen)
nfsd4_encode_user(struct svc_rqst *rqstp, kuid_t user, __be32 **p, int *buflen)
{
	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, user, INVALID_GID,
				 p, buflen);
}

static inline __be32
nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen)
nfsd4_encode_group(struct svc_rqst *rqstp, kgid_t group, __be32 **p, int *buflen)
{
	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
	return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, INVALID_UID, group,
				 p, buflen);
}

static inline __be32
nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
		__be32 **p, int *buflen)
{
	return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
	kuid_t uid = INVALID_UID;
	kgid_t gid = INVALID_GID;

	if (ace->whotype == NFS4_ACL_WHO_NAMED) {
		if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
			gid = ace->who_gid;
		else
			uid = ace->who_uid;
	}
	return nfsd4_encode_name(rqstp, ace->whotype, uid, gid, p, buflen);
}

#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
@@ -2224,9 +2235,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
			WRITE32(ace->type);
			WRITE32(ace->flag);
			WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
			status = nfsd4_encode_aclname(rqstp, ace->whotype,
				ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
				&p, &buflen);
			status = nfsd4_encode_aclname(rqstp, ace, &p, &buflen);
			if (status == nfserr_resource)
				goto out_resource;
			if (status)
+5 −1
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#define _LINUX_NFS4_H

#include <linux/list.h>
#include <linux/uidgid.h>
#include <uapi/linux/nfs4.h>

struct nfs4_ace {
@@ -20,7 +21,10 @@ struct nfs4_ace {
	uint32_t	flag;
	uint32_t	access_mask;
	int		whotype;
	uid_t		who;
	union {
		kuid_t	who_uid;
		kgid_t	who_gid;
	};
};

struct nfs4_acl {