Blame view

kernel/groups.c 4.91 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
30639b6af   Alexey Dobriyan   groups: move code...
2
3
4
5
  /*
   * Supplementary group IDs
   */
  #include <linux/cred.h>
9984de1a5   Paul Gortmaker   kernel: Map most ...
6
  #include <linux/export.h>
30639b6af   Alexey Dobriyan   groups: move code...
7
8
  #include <linux/slab.h>
  #include <linux/security.h>
b7b2562f7   Rasmus Villemoes   kernel/groups.c: ...
9
  #include <linux/sort.h>
30639b6af   Alexey Dobriyan   groups: move code...
10
  #include <linux/syscalls.h>
273d2c67c   Eric W. Biederman   userns: Don't all...
11
  #include <linux/user_namespace.h>
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
12
  #include <linux/vmalloc.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
13
  #include <linux/uaccess.h>
30639b6af   Alexey Dobriyan   groups: move code...
14

30639b6af   Alexey Dobriyan   groups: move code...
15
16
  struct group_info *groups_alloc(int gidsetsize)
  {
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
17
18
19
20
21
22
  	struct group_info *gi;
  	unsigned int len;
  
  	len = sizeof(struct group_info) + sizeof(kgid_t) * gidsetsize;
  	gi = kmalloc(len, GFP_KERNEL_ACCOUNT|__GFP_NOWARN|__GFP_NORETRY);
  	if (!gi)
88dca4ca5   Christoph Hellwig   mm: remove the pg...
23
  		gi = __vmalloc(len, GFP_KERNEL_ACCOUNT);
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
24
  	if (!gi)
30639b6af   Alexey Dobriyan   groups: move code...
25
  		return NULL;
30639b6af   Alexey Dobriyan   groups: move code...
26

81243eacf   Alexey Dobriyan   cred: simpler, 1D...
27
28
29
  	atomic_set(&gi->usage, 1);
  	gi->ngroups = gidsetsize;
  	return gi;
30639b6af   Alexey Dobriyan   groups: move code...
30
31
32
33
34
35
  }
  
  EXPORT_SYMBOL(groups_alloc);
  
  void groups_free(struct group_info *group_info)
  {
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
36
  	kvfree(group_info);
30639b6af   Alexey Dobriyan   groups: move code...
37
38
39
40
41
42
43
44
  }
  
  EXPORT_SYMBOL(groups_free);
  
  /* export the group_info to a user-space array */
  static int groups_to_user(gid_t __user *grouplist,
  			  const struct group_info *group_info)
  {
ae2975bc3   Eric W. Biederman   userns: Convert g...
45
  	struct user_namespace *user_ns = current_user_ns();
30639b6af   Alexey Dobriyan   groups: move code...
46
47
  	int i;
  	unsigned int count = group_info->ngroups;
ae2975bc3   Eric W. Biederman   userns: Convert g...
48
49
  	for (i = 0; i < count; i++) {
  		gid_t gid;
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
50
  		gid = from_kgid_munged(user_ns, group_info->gid[i]);
ae2975bc3   Eric W. Biederman   userns: Convert g...
51
  		if (put_user(gid, grouplist+i))
30639b6af   Alexey Dobriyan   groups: move code...
52
  			return -EFAULT;
30639b6af   Alexey Dobriyan   groups: move code...
53
54
55
56
57
58
59
60
  	}
  	return 0;
  }
  
  /* fill a group_info from a user-space array - it must be allocated already */
  static int groups_from_user(struct group_info *group_info,
      gid_t __user *grouplist)
  {
ae2975bc3   Eric W. Biederman   userns: Convert g...
61
  	struct user_namespace *user_ns = current_user_ns();
30639b6af   Alexey Dobriyan   groups: move code...
62
63
  	int i;
  	unsigned int count = group_info->ngroups;
ae2975bc3   Eric W. Biederman   userns: Convert g...
64
65
66
67
  	for (i = 0; i < count; i++) {
  		gid_t gid;
  		kgid_t kgid;
  		if (get_user(gid, grouplist+i))
30639b6af   Alexey Dobriyan   groups: move code...
68
  			return -EFAULT;
ae2975bc3   Eric W. Biederman   userns: Convert g...
69
70
71
  		kgid = make_kgid(user_ns, gid);
  		if (!gid_valid(kgid))
  			return -EINVAL;
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
72
  		group_info->gid[i] = kgid;
30639b6af   Alexey Dobriyan   groups: move code...
73
74
75
  	}
  	return 0;
  }
b7b2562f7   Rasmus Villemoes   kernel/groups.c: ...
76
77
78
79
80
81
82
  static int gid_cmp(const void *_a, const void *_b)
  {
  	kgid_t a = *(kgid_t *)_a;
  	kgid_t b = *(kgid_t *)_b;
  
  	return gid_gt(a, b) - gid_lt(a, b);
  }
bdcf0a423   Thiago Rafael Becker   kernel: make grou...
83
  void groups_sort(struct group_info *group_info)
30639b6af   Alexey Dobriyan   groups: move code...
84
  {
b7b2562f7   Rasmus Villemoes   kernel/groups.c: ...
85
86
  	sort(group_info->gid, group_info->ngroups, sizeof(*group_info->gid),
  	     gid_cmp, NULL);
30639b6af   Alexey Dobriyan   groups: move code...
87
  }
bdcf0a423   Thiago Rafael Becker   kernel: make grou...
88
  EXPORT_SYMBOL(groups_sort);
30639b6af   Alexey Dobriyan   groups: move code...
89
90
  
  /* a simple bsearch */
ae2975bc3   Eric W. Biederman   userns: Convert g...
91
  int groups_search(const struct group_info *group_info, kgid_t grp)
30639b6af   Alexey Dobriyan   groups: move code...
92
93
94
95
96
97
98
99
100
101
  {
  	unsigned int left, right;
  
  	if (!group_info)
  		return 0;
  
  	left = 0;
  	right = group_info->ngroups;
  	while (left < right) {
  		unsigned int mid = (left+right)/2;
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
102
  		if (gid_gt(grp, group_info->gid[mid]))
30639b6af   Alexey Dobriyan   groups: move code...
103
  			left = mid + 1;
81243eacf   Alexey Dobriyan   cred: simpler, 1D...
104
  		else if (gid_lt(grp, group_info->gid[mid]))
30639b6af   Alexey Dobriyan   groups: move code...
105
106
107
108
109
110
111
112
113
114
115
  			right = mid;
  		else
  			return 1;
  	}
  	return 0;
  }
  
  /**
   * set_groups - Change a group subscription in a set of credentials
   * @new: The newly prepared set of credentials to alter
   * @group_info: The group list to install
30639b6af   Alexey Dobriyan   groups: move code...
116
   */
8f6c5ffc8   Wang YanQing   kernel/groups.c: ...
117
  void set_groups(struct cred *new, struct group_info *group_info)
30639b6af   Alexey Dobriyan   groups: move code...
118
  {
30639b6af   Alexey Dobriyan   groups: move code...
119
  	put_group_info(new->group_info);
30639b6af   Alexey Dobriyan   groups: move code...
120
121
  	get_group_info(group_info);
  	new->group_info = group_info;
30639b6af   Alexey Dobriyan   groups: move code...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  }
  
  EXPORT_SYMBOL(set_groups);
  
  /**
   * set_current_groups - Change current's group subscription
   * @group_info: The group list to impose
   *
   * Validate a group subscription and, if valid, impose it upon current's task
   * security record.
   */
  int set_current_groups(struct group_info *group_info)
  {
  	struct cred *new;
30639b6af   Alexey Dobriyan   groups: move code...
136
137
138
139
  
  	new = prepare_creds();
  	if (!new)
  		return -ENOMEM;
8f6c5ffc8   Wang YanQing   kernel/groups.c: ...
140
  	set_groups(new, group_info);
30639b6af   Alexey Dobriyan   groups: move code...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  	return commit_creds(new);
  }
  
  EXPORT_SYMBOL(set_current_groups);
  
  SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
  {
  	const struct cred *cred = current_cred();
  	int i;
  
  	if (gidsetsize < 0)
  		return -EINVAL;
  
  	/* no need to grab task_lock here; it cannot change */
  	i = cred->group_info->ngroups;
  	if (gidsetsize) {
  		if (i > gidsetsize) {
  			i = -EINVAL;
  			goto out;
  		}
  		if (groups_to_user(grouplist, cred->group_info)) {
  			i = -EFAULT;
  			goto out;
  		}
  	}
  out:
  	return i;
  }
7ff4d90b4   Eric W. Biederman   groups: Consolida...
169
170
171
  bool may_setgroups(void)
  {
  	struct user_namespace *user_ns = current_user_ns();
111767c1d   Thomas Cedeno   LSM: Signal to Sa...
172
  	return ns_capable_setid(user_ns, CAP_SETGID) &&
273d2c67c   Eric W. Biederman   userns: Don't all...
173
  		userns_may_setgroups(user_ns);
7ff4d90b4   Eric W. Biederman   groups: Consolida...
174
  }
30639b6af   Alexey Dobriyan   groups: move code...
175
176
177
178
179
180
181
182
183
  /*
   *	SMP: Our groups are copy-on-write. We can set them safely
   *	without another task interfering.
   */
  
  SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
  {
  	struct group_info *group_info;
  	int retval;
7ff4d90b4   Eric W. Biederman   groups: Consolida...
184
  	if (!may_setgroups())
30639b6af   Alexey Dobriyan   groups: move code...
185
186
187
188
189
190
191
192
193
194
195
196
  		return -EPERM;
  	if ((unsigned)gidsetsize > NGROUPS_MAX)
  		return -EINVAL;
  
  	group_info = groups_alloc(gidsetsize);
  	if (!group_info)
  		return -ENOMEM;
  	retval = groups_from_user(group_info, grouplist);
  	if (retval) {
  		put_group_info(group_info);
  		return retval;
  	}
bdcf0a423   Thiago Rafael Becker   kernel: make grou...
197
  	groups_sort(group_info);
30639b6af   Alexey Dobriyan   groups: move code...
198
199
200
201
202
203
204
205
206
  	retval = set_current_groups(group_info);
  	put_group_info(group_info);
  
  	return retval;
  }
  
  /*
   * Check whether we're fsgid/egid or in the supplemental group..
   */
72cda3d1e   Eric W. Biederman   userns: Convert i...
207
  int in_group_p(kgid_t grp)
30639b6af   Alexey Dobriyan   groups: move code...
208
209
210
  {
  	const struct cred *cred = current_cred();
  	int retval = 1;
72cda3d1e   Eric W. Biederman   userns: Convert i...
211
212
  	if (!gid_eq(grp, cred->fsgid))
  		retval = groups_search(cred->group_info, grp);
30639b6af   Alexey Dobriyan   groups: move code...
213
214
215
216
  	return retval;
  }
  
  EXPORT_SYMBOL(in_group_p);
72cda3d1e   Eric W. Biederman   userns: Convert i...
217
  int in_egroup_p(kgid_t grp)
30639b6af   Alexey Dobriyan   groups: move code...
218
219
220
  {
  	const struct cred *cred = current_cred();
  	int retval = 1;
72cda3d1e   Eric W. Biederman   userns: Convert i...
221
222
  	if (!gid_eq(grp, cred->egid))
  		retval = groups_search(cred->group_info, grp);
30639b6af   Alexey Dobriyan   groups: move code...
223
224
225
226
  	return retval;
  }
  
  EXPORT_SYMBOL(in_egroup_p);