1/*
2 *  Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3 *  Copyright (C) 2007 The Regents of the University of California.
4 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5 *  Written by Brian Behlendorf <behlendorf1@llnl.gov>.
6 *  UCRL-CODE-235197
7 *
8 *  This file is part of the SPL, Solaris Porting Layer.
9 *
10 *  The SPL is free software; you can redistribute it and/or modify it
11 *  under the terms of the GNU General Public License as published by the
12 *  Free Software Foundation; either version 2 of the License, or (at your
13 *  option) any later version.
14 *
15 *  The SPL is distributed in the hope that it will be useful, but WITHOUT
16 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18 *  for more details.
19 *
20 *  You should have received a copy of the GNU General Public License along
21 *  with the SPL.  If not, see <http://www.gnu.org/licenses/>.
22 *
23 *  Solaris Porting Layer (SPL) Credential Implementation.
24 */
25
26#include <sys/cred.h>
27
28static int
29cr_groups_search(const struct group_info *group_info, kgid_t grp)
30{
31	unsigned int left, right, mid;
32	int cmp;
33
34	if (!group_info)
35		return (0);
36
37	left = 0;
38	right = group_info->ngroups;
39	while (left < right) {
40		mid = (left + right) / 2;
41		cmp = KGID_TO_SGID(grp) -
42		    KGID_TO_SGID(GROUP_AT(group_info, mid));
43
44		if (cmp > 0)
45			left = mid + 1;
46		else if (cmp < 0)
47			right = mid;
48		else
49			return (1);
50	}
51	return (0);
52}
53
54/* Hold a reference on the credential */
55void
56crhold(cred_t *cr)
57{
58	(void) get_cred((const cred_t *)cr);
59}
60
61/* Free a reference on the credential */
62void
63crfree(cred_t *cr)
64{
65	put_cred((const cred_t *)cr);
66}
67
68/* Return the number of supplemental groups */
69int
70crgetngroups(const cred_t *cr)
71{
72	struct group_info *gi;
73	int rc;
74
75	gi = cr->group_info;
76	rc = gi->ngroups;
77#ifndef HAVE_GROUP_INFO_GID
78	/*
79	 * For Linux <= 4.8,
80	 * crgetgroups will only returns gi->blocks[0], which contains only
81	 * the first NGROUPS_PER_BLOCK groups.
82	 */
83	if (rc > NGROUPS_PER_BLOCK) {
84		WARN_ON_ONCE(1);
85		rc = NGROUPS_PER_BLOCK;
86	}
87#endif
88	return (rc);
89}
90
91/*
92 * Return an array of supplemental gids.  The returned address is safe
93 * to use as long as the caller has taken a reference with crhold().
94 *
95 * Linux 4.9 API change, group_info changed from 2d array via ->blocks to 1d
96 * array via ->gid.
97 */
98gid_t *
99crgetgroups(const cred_t *cr)
100{
101	struct group_info *gi;
102	gid_t *gids = NULL;
103
104	gi = cr->group_info;
105#ifdef HAVE_GROUP_INFO_GID
106	gids = KGIDP_TO_SGIDP(gi->gid);
107#else
108	if (gi->nblocks > 0)
109		gids = KGIDP_TO_SGIDP(gi->blocks[0]);
110#endif
111	return (gids);
112}
113
114/* Check if the passed gid is available in supplied credential. */
115int
116groupmember(gid_t gid, const cred_t *cr)
117{
118	struct group_info *gi;
119	int rc;
120
121	gi = cr->group_info;
122	rc = cr_groups_search(gi, SGID_TO_KGID(gid));
123
124	return (rc);
125}
126
127/* Return the effective user id */
128uid_t
129crgetuid(const cred_t *cr)
130{
131	return (KUID_TO_SUID(cr->fsuid));
132}
133
134/* Return the real user id */
135uid_t
136crgetruid(const cred_t *cr)
137{
138	return (KUID_TO_SUID(cr->uid));
139}
140
141/* Return the effective group id */
142gid_t
143crgetgid(const cred_t *cr)
144{
145	return (KGID_TO_SGID(cr->fsgid));
146}
147
148/* Return the initial user ns or nop_mnt_idmap */
149zidmap_t *
150zfs_get_init_idmap(void)
151{
152#ifdef HAVE_IOPS_CREATE_IDMAP
153	return ((zidmap_t *)&nop_mnt_idmap);
154#else
155	return ((zidmap_t *)&init_user_ns);
156#endif
157}
158
159EXPORT_SYMBOL(zfs_get_init_idmap);
160EXPORT_SYMBOL(crhold);
161EXPORT_SYMBOL(crfree);
162EXPORT_SYMBOL(crgetuid);
163EXPORT_SYMBOL(crgetruid);
164EXPORT_SYMBOL(crgetgid);
165EXPORT_SYMBOL(crgetngroups);
166EXPORT_SYMBOL(crgetgroups);
167EXPORT_SYMBOL(groupmember);
168