Commit 00fcd1ceab6c42f7facfa0168b207fe70ee198c2
Committed by
Greg Kroah-Hartman
1 parent
5f20adeafc
Exists in
smarc-ti-linux-3.14.y
and in
1 other branch
groups: Consolidate the setgroups permission checks
commit 7ff4d90b4c24a03666f296c3d4878cd39001e81e upstream. Today there are 3 instances of setgroups and due to an oversight their permission checking has diverged. Add a common function so that they may all share the same permission checking code. This corrects the current oversight in the current permission checks and adds a helper to avoid this in the future. A user namespace security fix will update this new helper, shortly. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 4 changed files with 11 additions and 3 deletions Inline Diff
arch/s390/kernel/compat_linux.c
1 | /* | 1 | /* |
2 | * S390 version | 2 | * S390 version |
3 | * Copyright IBM Corp. 2000 | 3 | * Copyright IBM Corp. 2000 |
4 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), | 4 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), |
5 | * Gerhard Tonn (ton@de.ibm.com) | 5 | * Gerhard Tonn (ton@de.ibm.com) |
6 | * Thomas Spatzier (tspat@de.ibm.com) | 6 | * Thomas Spatzier (tspat@de.ibm.com) |
7 | * | 7 | * |
8 | * Conversion between 31bit and 64bit native syscalls. | 8 | * Conversion between 31bit and 64bit native syscalls. |
9 | * | 9 | * |
10 | * Heavily inspired by the 32-bit Sparc compat code which is | 10 | * Heavily inspired by the 32-bit Sparc compat code which is |
11 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | 11 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) |
12 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | 12 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) |
13 | * | 13 | * |
14 | */ | 14 | */ |
15 | 15 | ||
16 | 16 | ||
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
19 | #include <linux/fs.h> | 19 | #include <linux/fs.h> |
20 | #include <linux/mm.h> | 20 | #include <linux/mm.h> |
21 | #include <linux/file.h> | 21 | #include <linux/file.h> |
22 | #include <linux/signal.h> | 22 | #include <linux/signal.h> |
23 | #include <linux/resource.h> | 23 | #include <linux/resource.h> |
24 | #include <linux/times.h> | 24 | #include <linux/times.h> |
25 | #include <linux/smp.h> | 25 | #include <linux/smp.h> |
26 | #include <linux/sem.h> | 26 | #include <linux/sem.h> |
27 | #include <linux/msg.h> | 27 | #include <linux/msg.h> |
28 | #include <linux/shm.h> | 28 | #include <linux/shm.h> |
29 | #include <linux/uio.h> | 29 | #include <linux/uio.h> |
30 | #include <linux/quota.h> | 30 | #include <linux/quota.h> |
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/poll.h> | 32 | #include <linux/poll.h> |
33 | #include <linux/personality.h> | 33 | #include <linux/personality.h> |
34 | #include <linux/stat.h> | 34 | #include <linux/stat.h> |
35 | #include <linux/filter.h> | 35 | #include <linux/filter.h> |
36 | #include <linux/highmem.h> | 36 | #include <linux/highmem.h> |
37 | #include <linux/highuid.h> | 37 | #include <linux/highuid.h> |
38 | #include <linux/mman.h> | 38 | #include <linux/mman.h> |
39 | #include <linux/ipv6.h> | 39 | #include <linux/ipv6.h> |
40 | #include <linux/in.h> | 40 | #include <linux/in.h> |
41 | #include <linux/icmpv6.h> | 41 | #include <linux/icmpv6.h> |
42 | #include <linux/syscalls.h> | 42 | #include <linux/syscalls.h> |
43 | #include <linux/sysctl.h> | 43 | #include <linux/sysctl.h> |
44 | #include <linux/binfmts.h> | 44 | #include <linux/binfmts.h> |
45 | #include <linux/capability.h> | 45 | #include <linux/capability.h> |
46 | #include <linux/compat.h> | 46 | #include <linux/compat.h> |
47 | #include <linux/vfs.h> | 47 | #include <linux/vfs.h> |
48 | #include <linux/ptrace.h> | 48 | #include <linux/ptrace.h> |
49 | #include <linux/fadvise.h> | 49 | #include <linux/fadvise.h> |
50 | #include <linux/ipc.h> | 50 | #include <linux/ipc.h> |
51 | #include <linux/slab.h> | 51 | #include <linux/slab.h> |
52 | 52 | ||
53 | #include <asm/types.h> | 53 | #include <asm/types.h> |
54 | #include <asm/uaccess.h> | 54 | #include <asm/uaccess.h> |
55 | 55 | ||
56 | #include <net/scm.h> | 56 | #include <net/scm.h> |
57 | #include <net/sock.h> | 57 | #include <net/sock.h> |
58 | 58 | ||
59 | #include "compat_linux.h" | 59 | #include "compat_linux.h" |
60 | 60 | ||
61 | /* For this source file, we want overflow handling. */ | 61 | /* For this source file, we want overflow handling. */ |
62 | 62 | ||
63 | #undef high2lowuid | 63 | #undef high2lowuid |
64 | #undef high2lowgid | 64 | #undef high2lowgid |
65 | #undef low2highuid | 65 | #undef low2highuid |
66 | #undef low2highgid | 66 | #undef low2highgid |
67 | #undef SET_UID16 | 67 | #undef SET_UID16 |
68 | #undef SET_GID16 | 68 | #undef SET_GID16 |
69 | #undef NEW_TO_OLD_UID | 69 | #undef NEW_TO_OLD_UID |
70 | #undef NEW_TO_OLD_GID | 70 | #undef NEW_TO_OLD_GID |
71 | #undef SET_OLDSTAT_UID | 71 | #undef SET_OLDSTAT_UID |
72 | #undef SET_OLDSTAT_GID | 72 | #undef SET_OLDSTAT_GID |
73 | #undef SET_STAT_UID | 73 | #undef SET_STAT_UID |
74 | #undef SET_STAT_GID | 74 | #undef SET_STAT_GID |
75 | 75 | ||
76 | #define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) | 76 | #define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) |
77 | #define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) | 77 | #define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) |
78 | #define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid) | 78 | #define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid) |
79 | #define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid) | 79 | #define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid) |
80 | #define SET_UID16(var, uid) var = high2lowuid(uid) | 80 | #define SET_UID16(var, uid) var = high2lowuid(uid) |
81 | #define SET_GID16(var, gid) var = high2lowgid(gid) | 81 | #define SET_GID16(var, gid) var = high2lowgid(gid) |
82 | #define NEW_TO_OLD_UID(uid) high2lowuid(uid) | 82 | #define NEW_TO_OLD_UID(uid) high2lowuid(uid) |
83 | #define NEW_TO_OLD_GID(gid) high2lowgid(gid) | 83 | #define NEW_TO_OLD_GID(gid) high2lowgid(gid) |
84 | #define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) | 84 | #define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) |
85 | #define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) | 85 | #define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) |
86 | #define SET_STAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) | 86 | #define SET_STAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) |
87 | #define SET_STAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) | 87 | #define SET_STAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) |
88 | 88 | ||
89 | asmlinkage long sys32_chown16(const char __user * filename, u16 user, u16 group) | 89 | asmlinkage long sys32_chown16(const char __user * filename, u16 user, u16 group) |
90 | { | 90 | { |
91 | return sys_chown(filename, low2highuid(user), low2highgid(group)); | 91 | return sys_chown(filename, low2highuid(user), low2highgid(group)); |
92 | } | 92 | } |
93 | 93 | ||
94 | asmlinkage long sys32_lchown16(const char __user * filename, u16 user, u16 group) | 94 | asmlinkage long sys32_lchown16(const char __user * filename, u16 user, u16 group) |
95 | { | 95 | { |
96 | return sys_lchown(filename, low2highuid(user), low2highgid(group)); | 96 | return sys_lchown(filename, low2highuid(user), low2highgid(group)); |
97 | } | 97 | } |
98 | 98 | ||
99 | asmlinkage long sys32_fchown16(unsigned int fd, u16 user, u16 group) | 99 | asmlinkage long sys32_fchown16(unsigned int fd, u16 user, u16 group) |
100 | { | 100 | { |
101 | return sys_fchown(fd, low2highuid(user), low2highgid(group)); | 101 | return sys_fchown(fd, low2highuid(user), low2highgid(group)); |
102 | } | 102 | } |
103 | 103 | ||
104 | asmlinkage long sys32_setregid16(u16 rgid, u16 egid) | 104 | asmlinkage long sys32_setregid16(u16 rgid, u16 egid) |
105 | { | 105 | { |
106 | return sys_setregid(low2highgid(rgid), low2highgid(egid)); | 106 | return sys_setregid(low2highgid(rgid), low2highgid(egid)); |
107 | } | 107 | } |
108 | 108 | ||
109 | asmlinkage long sys32_setgid16(u16 gid) | 109 | asmlinkage long sys32_setgid16(u16 gid) |
110 | { | 110 | { |
111 | return sys_setgid((gid_t)gid); | 111 | return sys_setgid((gid_t)gid); |
112 | } | 112 | } |
113 | 113 | ||
114 | asmlinkage long sys32_setreuid16(u16 ruid, u16 euid) | 114 | asmlinkage long sys32_setreuid16(u16 ruid, u16 euid) |
115 | { | 115 | { |
116 | return sys_setreuid(low2highuid(ruid), low2highuid(euid)); | 116 | return sys_setreuid(low2highuid(ruid), low2highuid(euid)); |
117 | } | 117 | } |
118 | 118 | ||
119 | asmlinkage long sys32_setuid16(u16 uid) | 119 | asmlinkage long sys32_setuid16(u16 uid) |
120 | { | 120 | { |
121 | return sys_setuid((uid_t)uid); | 121 | return sys_setuid((uid_t)uid); |
122 | } | 122 | } |
123 | 123 | ||
124 | asmlinkage long sys32_setresuid16(u16 ruid, u16 euid, u16 suid) | 124 | asmlinkage long sys32_setresuid16(u16 ruid, u16 euid, u16 suid) |
125 | { | 125 | { |
126 | return sys_setresuid(low2highuid(ruid), low2highuid(euid), | 126 | return sys_setresuid(low2highuid(ruid), low2highuid(euid), |
127 | low2highuid(suid)); | 127 | low2highuid(suid)); |
128 | } | 128 | } |
129 | 129 | ||
130 | asmlinkage long sys32_getresuid16(u16 __user *ruidp, u16 __user *euidp, u16 __user *suidp) | 130 | asmlinkage long sys32_getresuid16(u16 __user *ruidp, u16 __user *euidp, u16 __user *suidp) |
131 | { | 131 | { |
132 | const struct cred *cred = current_cred(); | 132 | const struct cred *cred = current_cred(); |
133 | int retval; | 133 | int retval; |
134 | u16 ruid, euid, suid; | 134 | u16 ruid, euid, suid; |
135 | 135 | ||
136 | ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid)); | 136 | ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid)); |
137 | euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid)); | 137 | euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid)); |
138 | suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid)); | 138 | suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid)); |
139 | 139 | ||
140 | if (!(retval = put_user(ruid, ruidp)) && | 140 | if (!(retval = put_user(ruid, ruidp)) && |
141 | !(retval = put_user(euid, euidp))) | 141 | !(retval = put_user(euid, euidp))) |
142 | retval = put_user(suid, suidp); | 142 | retval = put_user(suid, suidp); |
143 | 143 | ||
144 | return retval; | 144 | return retval; |
145 | } | 145 | } |
146 | 146 | ||
147 | asmlinkage long sys32_setresgid16(u16 rgid, u16 egid, u16 sgid) | 147 | asmlinkage long sys32_setresgid16(u16 rgid, u16 egid, u16 sgid) |
148 | { | 148 | { |
149 | return sys_setresgid(low2highgid(rgid), low2highgid(egid), | 149 | return sys_setresgid(low2highgid(rgid), low2highgid(egid), |
150 | low2highgid(sgid)); | 150 | low2highgid(sgid)); |
151 | } | 151 | } |
152 | 152 | ||
153 | asmlinkage long sys32_getresgid16(u16 __user *rgidp, u16 __user *egidp, u16 __user *sgidp) | 153 | asmlinkage long sys32_getresgid16(u16 __user *rgidp, u16 __user *egidp, u16 __user *sgidp) |
154 | { | 154 | { |
155 | const struct cred *cred = current_cred(); | 155 | const struct cred *cred = current_cred(); |
156 | int retval; | 156 | int retval; |
157 | u16 rgid, egid, sgid; | 157 | u16 rgid, egid, sgid; |
158 | 158 | ||
159 | rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid)); | 159 | rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid)); |
160 | egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid)); | 160 | egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid)); |
161 | sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid)); | 161 | sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid)); |
162 | 162 | ||
163 | if (!(retval = put_user(rgid, rgidp)) && | 163 | if (!(retval = put_user(rgid, rgidp)) && |
164 | !(retval = put_user(egid, egidp))) | 164 | !(retval = put_user(egid, egidp))) |
165 | retval = put_user(sgid, sgidp); | 165 | retval = put_user(sgid, sgidp); |
166 | 166 | ||
167 | return retval; | 167 | return retval; |
168 | } | 168 | } |
169 | 169 | ||
170 | asmlinkage long sys32_setfsuid16(u16 uid) | 170 | asmlinkage long sys32_setfsuid16(u16 uid) |
171 | { | 171 | { |
172 | return sys_setfsuid((uid_t)uid); | 172 | return sys_setfsuid((uid_t)uid); |
173 | } | 173 | } |
174 | 174 | ||
175 | asmlinkage long sys32_setfsgid16(u16 gid) | 175 | asmlinkage long sys32_setfsgid16(u16 gid) |
176 | { | 176 | { |
177 | return sys_setfsgid((gid_t)gid); | 177 | return sys_setfsgid((gid_t)gid); |
178 | } | 178 | } |
179 | 179 | ||
180 | static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info) | 180 | static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info) |
181 | { | 181 | { |
182 | struct user_namespace *user_ns = current_user_ns(); | 182 | struct user_namespace *user_ns = current_user_ns(); |
183 | int i; | 183 | int i; |
184 | u16 group; | 184 | u16 group; |
185 | kgid_t kgid; | 185 | kgid_t kgid; |
186 | 186 | ||
187 | for (i = 0; i < group_info->ngroups; i++) { | 187 | for (i = 0; i < group_info->ngroups; i++) { |
188 | kgid = GROUP_AT(group_info, i); | 188 | kgid = GROUP_AT(group_info, i); |
189 | group = (u16)from_kgid_munged(user_ns, kgid); | 189 | group = (u16)from_kgid_munged(user_ns, kgid); |
190 | if (put_user(group, grouplist+i)) | 190 | if (put_user(group, grouplist+i)) |
191 | return -EFAULT; | 191 | return -EFAULT; |
192 | } | 192 | } |
193 | 193 | ||
194 | return 0; | 194 | return 0; |
195 | } | 195 | } |
196 | 196 | ||
197 | static int groups16_from_user(struct group_info *group_info, u16 __user *grouplist) | 197 | static int groups16_from_user(struct group_info *group_info, u16 __user *grouplist) |
198 | { | 198 | { |
199 | struct user_namespace *user_ns = current_user_ns(); | 199 | struct user_namespace *user_ns = current_user_ns(); |
200 | int i; | 200 | int i; |
201 | u16 group; | 201 | u16 group; |
202 | kgid_t kgid; | 202 | kgid_t kgid; |
203 | 203 | ||
204 | for (i = 0; i < group_info->ngroups; i++) { | 204 | for (i = 0; i < group_info->ngroups; i++) { |
205 | if (get_user(group, grouplist+i)) | 205 | if (get_user(group, grouplist+i)) |
206 | return -EFAULT; | 206 | return -EFAULT; |
207 | 207 | ||
208 | kgid = make_kgid(user_ns, (gid_t)group); | 208 | kgid = make_kgid(user_ns, (gid_t)group); |
209 | if (!gid_valid(kgid)) | 209 | if (!gid_valid(kgid)) |
210 | return -EINVAL; | 210 | return -EINVAL; |
211 | 211 | ||
212 | GROUP_AT(group_info, i) = kgid; | 212 | GROUP_AT(group_info, i) = kgid; |
213 | } | 213 | } |
214 | 214 | ||
215 | return 0; | 215 | return 0; |
216 | } | 216 | } |
217 | 217 | ||
218 | asmlinkage long sys32_getgroups16(int gidsetsize, u16 __user *grouplist) | 218 | asmlinkage long sys32_getgroups16(int gidsetsize, u16 __user *grouplist) |
219 | { | 219 | { |
220 | const struct cred *cred = current_cred(); | 220 | const struct cred *cred = current_cred(); |
221 | int i; | 221 | int i; |
222 | 222 | ||
223 | if (gidsetsize < 0) | 223 | if (gidsetsize < 0) |
224 | return -EINVAL; | 224 | return -EINVAL; |
225 | 225 | ||
226 | get_group_info(cred->group_info); | 226 | get_group_info(cred->group_info); |
227 | i = cred->group_info->ngroups; | 227 | i = cred->group_info->ngroups; |
228 | if (gidsetsize) { | 228 | if (gidsetsize) { |
229 | if (i > gidsetsize) { | 229 | if (i > gidsetsize) { |
230 | i = -EINVAL; | 230 | i = -EINVAL; |
231 | goto out; | 231 | goto out; |
232 | } | 232 | } |
233 | if (groups16_to_user(grouplist, cred->group_info)) { | 233 | if (groups16_to_user(grouplist, cred->group_info)) { |
234 | i = -EFAULT; | 234 | i = -EFAULT; |
235 | goto out; | 235 | goto out; |
236 | } | 236 | } |
237 | } | 237 | } |
238 | out: | 238 | out: |
239 | put_group_info(cred->group_info); | 239 | put_group_info(cred->group_info); |
240 | return i; | 240 | return i; |
241 | } | 241 | } |
242 | 242 | ||
243 | asmlinkage long sys32_setgroups16(int gidsetsize, u16 __user *grouplist) | 243 | asmlinkage long sys32_setgroups16(int gidsetsize, u16 __user *grouplist) |
244 | { | 244 | { |
245 | struct group_info *group_info; | 245 | struct group_info *group_info; |
246 | int retval; | 246 | int retval; |
247 | 247 | ||
248 | if (!capable(CAP_SETGID)) | 248 | if (!may_setgroups()) |
249 | return -EPERM; | 249 | return -EPERM; |
250 | if ((unsigned)gidsetsize > NGROUPS_MAX) | 250 | if ((unsigned)gidsetsize > NGROUPS_MAX) |
251 | return -EINVAL; | 251 | return -EINVAL; |
252 | 252 | ||
253 | group_info = groups_alloc(gidsetsize); | 253 | group_info = groups_alloc(gidsetsize); |
254 | if (!group_info) | 254 | if (!group_info) |
255 | return -ENOMEM; | 255 | return -ENOMEM; |
256 | retval = groups16_from_user(group_info, grouplist); | 256 | retval = groups16_from_user(group_info, grouplist); |
257 | if (retval) { | 257 | if (retval) { |
258 | put_group_info(group_info); | 258 | put_group_info(group_info); |
259 | return retval; | 259 | return retval; |
260 | } | 260 | } |
261 | 261 | ||
262 | retval = set_current_groups(group_info); | 262 | retval = set_current_groups(group_info); |
263 | put_group_info(group_info); | 263 | put_group_info(group_info); |
264 | 264 | ||
265 | return retval; | 265 | return retval; |
266 | } | 266 | } |
267 | 267 | ||
268 | asmlinkage long sys32_getuid16(void) | 268 | asmlinkage long sys32_getuid16(void) |
269 | { | 269 | { |
270 | return high2lowuid(from_kuid_munged(current_user_ns(), current_uid())); | 270 | return high2lowuid(from_kuid_munged(current_user_ns(), current_uid())); |
271 | } | 271 | } |
272 | 272 | ||
273 | asmlinkage long sys32_geteuid16(void) | 273 | asmlinkage long sys32_geteuid16(void) |
274 | { | 274 | { |
275 | return high2lowuid(from_kuid_munged(current_user_ns(), current_euid())); | 275 | return high2lowuid(from_kuid_munged(current_user_ns(), current_euid())); |
276 | } | 276 | } |
277 | 277 | ||
278 | asmlinkage long sys32_getgid16(void) | 278 | asmlinkage long sys32_getgid16(void) |
279 | { | 279 | { |
280 | return high2lowgid(from_kgid_munged(current_user_ns(), current_gid())); | 280 | return high2lowgid(from_kgid_munged(current_user_ns(), current_gid())); |
281 | } | 281 | } |
282 | 282 | ||
283 | asmlinkage long sys32_getegid16(void) | 283 | asmlinkage long sys32_getegid16(void) |
284 | { | 284 | { |
285 | return high2lowgid(from_kgid_munged(current_user_ns(), current_egid())); | 285 | return high2lowgid(from_kgid_munged(current_user_ns(), current_egid())); |
286 | } | 286 | } |
287 | 287 | ||
288 | #ifdef CONFIG_SYSVIPC | 288 | #ifdef CONFIG_SYSVIPC |
289 | COMPAT_SYSCALL_DEFINE5(s390_ipc, uint, call, int, first, compat_ulong_t, second, | 289 | COMPAT_SYSCALL_DEFINE5(s390_ipc, uint, call, int, first, compat_ulong_t, second, |
290 | compat_ulong_t, third, compat_uptr_t, ptr) | 290 | compat_ulong_t, third, compat_uptr_t, ptr) |
291 | { | 291 | { |
292 | if (call >> 16) /* hack for backward compatibility */ | 292 | if (call >> 16) /* hack for backward compatibility */ |
293 | return -EINVAL; | 293 | return -EINVAL; |
294 | return compat_sys_ipc(call, first, second, third, ptr, third); | 294 | return compat_sys_ipc(call, first, second, third, ptr, third); |
295 | } | 295 | } |
296 | #endif | 296 | #endif |
297 | 297 | ||
298 | asmlinkage long sys32_truncate64(const char __user * path, unsigned long high, unsigned long low) | 298 | asmlinkage long sys32_truncate64(const char __user * path, unsigned long high, unsigned long low) |
299 | { | 299 | { |
300 | if ((int)high < 0) | 300 | if ((int)high < 0) |
301 | return -EINVAL; | 301 | return -EINVAL; |
302 | else | 302 | else |
303 | return sys_truncate(path, (high << 32) | low); | 303 | return sys_truncate(path, (high << 32) | low); |
304 | } | 304 | } |
305 | 305 | ||
306 | asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long high, unsigned long low) | 306 | asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long high, unsigned long low) |
307 | { | 307 | { |
308 | if ((int)high < 0) | 308 | if ((int)high < 0) |
309 | return -EINVAL; | 309 | return -EINVAL; |
310 | else | 310 | else |
311 | return sys_ftruncate(fd, (high << 32) | low); | 311 | return sys_ftruncate(fd, (high << 32) | low); |
312 | } | 312 | } |
313 | 313 | ||
314 | asmlinkage long sys32_pread64(unsigned int fd, char __user *ubuf, | 314 | asmlinkage long sys32_pread64(unsigned int fd, char __user *ubuf, |
315 | size_t count, u32 poshi, u32 poslo) | 315 | size_t count, u32 poshi, u32 poslo) |
316 | { | 316 | { |
317 | if ((compat_ssize_t) count < 0) | 317 | if ((compat_ssize_t) count < 0) |
318 | return -EINVAL; | 318 | return -EINVAL; |
319 | return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); | 319 | return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); |
320 | } | 320 | } |
321 | 321 | ||
322 | asmlinkage long sys32_pwrite64(unsigned int fd, const char __user *ubuf, | 322 | asmlinkage long sys32_pwrite64(unsigned int fd, const char __user *ubuf, |
323 | size_t count, u32 poshi, u32 poslo) | 323 | size_t count, u32 poshi, u32 poslo) |
324 | { | 324 | { |
325 | if ((compat_ssize_t) count < 0) | 325 | if ((compat_ssize_t) count < 0) |
326 | return -EINVAL; | 326 | return -EINVAL; |
327 | return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); | 327 | return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); |
328 | } | 328 | } |
329 | 329 | ||
330 | asmlinkage compat_ssize_t sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count) | 330 | asmlinkage compat_ssize_t sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count) |
331 | { | 331 | { |
332 | return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count); | 332 | return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count); |
333 | } | 333 | } |
334 | 334 | ||
335 | struct stat64_emu31 { | 335 | struct stat64_emu31 { |
336 | unsigned long long st_dev; | 336 | unsigned long long st_dev; |
337 | unsigned int __pad1; | 337 | unsigned int __pad1; |
338 | #define STAT64_HAS_BROKEN_ST_INO 1 | 338 | #define STAT64_HAS_BROKEN_ST_INO 1 |
339 | u32 __st_ino; | 339 | u32 __st_ino; |
340 | unsigned int st_mode; | 340 | unsigned int st_mode; |
341 | unsigned int st_nlink; | 341 | unsigned int st_nlink; |
342 | u32 st_uid; | 342 | u32 st_uid; |
343 | u32 st_gid; | 343 | u32 st_gid; |
344 | unsigned long long st_rdev; | 344 | unsigned long long st_rdev; |
345 | unsigned int __pad3; | 345 | unsigned int __pad3; |
346 | long st_size; | 346 | long st_size; |
347 | u32 st_blksize; | 347 | u32 st_blksize; |
348 | unsigned char __pad4[4]; | 348 | unsigned char __pad4[4]; |
349 | u32 __pad5; /* future possible st_blocks high bits */ | 349 | u32 __pad5; /* future possible st_blocks high bits */ |
350 | u32 st_blocks; /* Number 512-byte blocks allocated. */ | 350 | u32 st_blocks; /* Number 512-byte blocks allocated. */ |
351 | u32 st_atime; | 351 | u32 st_atime; |
352 | u32 __pad6; | 352 | u32 __pad6; |
353 | u32 st_mtime; | 353 | u32 st_mtime; |
354 | u32 __pad7; | 354 | u32 __pad7; |
355 | u32 st_ctime; | 355 | u32 st_ctime; |
356 | u32 __pad8; /* will be high 32 bits of ctime someday */ | 356 | u32 __pad8; /* will be high 32 bits of ctime someday */ |
357 | unsigned long st_ino; | 357 | unsigned long st_ino; |
358 | }; | 358 | }; |
359 | 359 | ||
360 | static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat) | 360 | static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat) |
361 | { | 361 | { |
362 | struct stat64_emu31 tmp; | 362 | struct stat64_emu31 tmp; |
363 | 363 | ||
364 | memset(&tmp, 0, sizeof(tmp)); | 364 | memset(&tmp, 0, sizeof(tmp)); |
365 | 365 | ||
366 | tmp.st_dev = huge_encode_dev(stat->dev); | 366 | tmp.st_dev = huge_encode_dev(stat->dev); |
367 | tmp.st_ino = stat->ino; | 367 | tmp.st_ino = stat->ino; |
368 | tmp.__st_ino = (u32)stat->ino; | 368 | tmp.__st_ino = (u32)stat->ino; |
369 | tmp.st_mode = stat->mode; | 369 | tmp.st_mode = stat->mode; |
370 | tmp.st_nlink = (unsigned int)stat->nlink; | 370 | tmp.st_nlink = (unsigned int)stat->nlink; |
371 | tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid); | 371 | tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid); |
372 | tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid); | 372 | tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid); |
373 | tmp.st_rdev = huge_encode_dev(stat->rdev); | 373 | tmp.st_rdev = huge_encode_dev(stat->rdev); |
374 | tmp.st_size = stat->size; | 374 | tmp.st_size = stat->size; |
375 | tmp.st_blksize = (u32)stat->blksize; | 375 | tmp.st_blksize = (u32)stat->blksize; |
376 | tmp.st_blocks = (u32)stat->blocks; | 376 | tmp.st_blocks = (u32)stat->blocks; |
377 | tmp.st_atime = (u32)stat->atime.tv_sec; | 377 | tmp.st_atime = (u32)stat->atime.tv_sec; |
378 | tmp.st_mtime = (u32)stat->mtime.tv_sec; | 378 | tmp.st_mtime = (u32)stat->mtime.tv_sec; |
379 | tmp.st_ctime = (u32)stat->ctime.tv_sec; | 379 | tmp.st_ctime = (u32)stat->ctime.tv_sec; |
380 | 380 | ||
381 | return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; | 381 | return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; |
382 | } | 382 | } |
383 | 383 | ||
384 | asmlinkage long sys32_stat64(const char __user * filename, struct stat64_emu31 __user * statbuf) | 384 | asmlinkage long sys32_stat64(const char __user * filename, struct stat64_emu31 __user * statbuf) |
385 | { | 385 | { |
386 | struct kstat stat; | 386 | struct kstat stat; |
387 | int ret = vfs_stat(filename, &stat); | 387 | int ret = vfs_stat(filename, &stat); |
388 | if (!ret) | 388 | if (!ret) |
389 | ret = cp_stat64(statbuf, &stat); | 389 | ret = cp_stat64(statbuf, &stat); |
390 | return ret; | 390 | return ret; |
391 | } | 391 | } |
392 | 392 | ||
393 | asmlinkage long sys32_lstat64(const char __user * filename, struct stat64_emu31 __user * statbuf) | 393 | asmlinkage long sys32_lstat64(const char __user * filename, struct stat64_emu31 __user * statbuf) |
394 | { | 394 | { |
395 | struct kstat stat; | 395 | struct kstat stat; |
396 | int ret = vfs_lstat(filename, &stat); | 396 | int ret = vfs_lstat(filename, &stat); |
397 | if (!ret) | 397 | if (!ret) |
398 | ret = cp_stat64(statbuf, &stat); | 398 | ret = cp_stat64(statbuf, &stat); |
399 | return ret; | 399 | return ret; |
400 | } | 400 | } |
401 | 401 | ||
402 | asmlinkage long sys32_fstat64(unsigned long fd, struct stat64_emu31 __user * statbuf) | 402 | asmlinkage long sys32_fstat64(unsigned long fd, struct stat64_emu31 __user * statbuf) |
403 | { | 403 | { |
404 | struct kstat stat; | 404 | struct kstat stat; |
405 | int ret = vfs_fstat(fd, &stat); | 405 | int ret = vfs_fstat(fd, &stat); |
406 | if (!ret) | 406 | if (!ret) |
407 | ret = cp_stat64(statbuf, &stat); | 407 | ret = cp_stat64(statbuf, &stat); |
408 | return ret; | 408 | return ret; |
409 | } | 409 | } |
410 | 410 | ||
411 | asmlinkage long sys32_fstatat64(unsigned int dfd, const char __user *filename, | 411 | asmlinkage long sys32_fstatat64(unsigned int dfd, const char __user *filename, |
412 | struct stat64_emu31 __user* statbuf, int flag) | 412 | struct stat64_emu31 __user* statbuf, int flag) |
413 | { | 413 | { |
414 | struct kstat stat; | 414 | struct kstat stat; |
415 | int error; | 415 | int error; |
416 | 416 | ||
417 | error = vfs_fstatat(dfd, filename, &stat, flag); | 417 | error = vfs_fstatat(dfd, filename, &stat, flag); |
418 | if (error) | 418 | if (error) |
419 | return error; | 419 | return error; |
420 | return cp_stat64(statbuf, &stat); | 420 | return cp_stat64(statbuf, &stat); |
421 | } | 421 | } |
422 | 422 | ||
423 | /* | 423 | /* |
424 | * Linux/i386 didn't use to be able to handle more than | 424 | * Linux/i386 didn't use to be able to handle more than |
425 | * 4 system call parameters, so these system calls used a memory | 425 | * 4 system call parameters, so these system calls used a memory |
426 | * block for parameter passing.. | 426 | * block for parameter passing.. |
427 | */ | 427 | */ |
428 | 428 | ||
429 | struct mmap_arg_struct_emu31 { | 429 | struct mmap_arg_struct_emu31 { |
430 | compat_ulong_t addr; | 430 | compat_ulong_t addr; |
431 | compat_ulong_t len; | 431 | compat_ulong_t len; |
432 | compat_ulong_t prot; | 432 | compat_ulong_t prot; |
433 | compat_ulong_t flags; | 433 | compat_ulong_t flags; |
434 | compat_ulong_t fd; | 434 | compat_ulong_t fd; |
435 | compat_ulong_t offset; | 435 | compat_ulong_t offset; |
436 | }; | 436 | }; |
437 | 437 | ||
438 | asmlinkage unsigned long old32_mmap(struct mmap_arg_struct_emu31 __user *arg) | 438 | asmlinkage unsigned long old32_mmap(struct mmap_arg_struct_emu31 __user *arg) |
439 | { | 439 | { |
440 | struct mmap_arg_struct_emu31 a; | 440 | struct mmap_arg_struct_emu31 a; |
441 | 441 | ||
442 | if (copy_from_user(&a, arg, sizeof(a))) | 442 | if (copy_from_user(&a, arg, sizeof(a))) |
443 | return -EFAULT; | 443 | return -EFAULT; |
444 | if (a.offset & ~PAGE_MASK) | 444 | if (a.offset & ~PAGE_MASK) |
445 | return -EINVAL; | 445 | return -EINVAL; |
446 | return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, | 446 | return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, |
447 | a.offset >> PAGE_SHIFT); | 447 | a.offset >> PAGE_SHIFT); |
448 | } | 448 | } |
449 | 449 | ||
450 | asmlinkage long sys32_mmap2(struct mmap_arg_struct_emu31 __user *arg) | 450 | asmlinkage long sys32_mmap2(struct mmap_arg_struct_emu31 __user *arg) |
451 | { | 451 | { |
452 | struct mmap_arg_struct_emu31 a; | 452 | struct mmap_arg_struct_emu31 a; |
453 | 453 | ||
454 | if (copy_from_user(&a, arg, sizeof(a))) | 454 | if (copy_from_user(&a, arg, sizeof(a))) |
455 | return -EFAULT; | 455 | return -EFAULT; |
456 | return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset); | 456 | return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset); |
457 | } | 457 | } |
458 | 458 | ||
459 | asmlinkage long sys32_read(unsigned int fd, char __user * buf, size_t count) | 459 | asmlinkage long sys32_read(unsigned int fd, char __user * buf, size_t count) |
460 | { | 460 | { |
461 | if ((compat_ssize_t) count < 0) | 461 | if ((compat_ssize_t) count < 0) |
462 | return -EINVAL; | 462 | return -EINVAL; |
463 | 463 | ||
464 | return sys_read(fd, buf, count); | 464 | return sys_read(fd, buf, count); |
465 | } | 465 | } |
466 | 466 | ||
467 | asmlinkage long sys32_write(unsigned int fd, const char __user * buf, size_t count) | 467 | asmlinkage long sys32_write(unsigned int fd, const char __user * buf, size_t count) |
468 | { | 468 | { |
469 | if ((compat_ssize_t) count < 0) | 469 | if ((compat_ssize_t) count < 0) |
470 | return -EINVAL; | 470 | return -EINVAL; |
471 | 471 | ||
472 | return sys_write(fd, buf, count); | 472 | return sys_write(fd, buf, count); |
473 | } | 473 | } |
474 | 474 | ||
475 | /* | 475 | /* |
476 | * 31 bit emulation wrapper functions for sys_fadvise64/fadvise64_64. | 476 | * 31 bit emulation wrapper functions for sys_fadvise64/fadvise64_64. |
477 | * These need to rewrite the advise values for POSIX_FADV_{DONTNEED,NOREUSE} | 477 | * These need to rewrite the advise values for POSIX_FADV_{DONTNEED,NOREUSE} |
478 | * because the 31 bit values differ from the 64 bit values. | 478 | * because the 31 bit values differ from the 64 bit values. |
479 | */ | 479 | */ |
480 | 480 | ||
481 | asmlinkage long | 481 | asmlinkage long |
482 | sys32_fadvise64(int fd, loff_t offset, size_t len, int advise) | 482 | sys32_fadvise64(int fd, loff_t offset, size_t len, int advise) |
483 | { | 483 | { |
484 | if (advise == 4) | 484 | if (advise == 4) |
485 | advise = POSIX_FADV_DONTNEED; | 485 | advise = POSIX_FADV_DONTNEED; |
486 | else if (advise == 5) | 486 | else if (advise == 5) |
487 | advise = POSIX_FADV_NOREUSE; | 487 | advise = POSIX_FADV_NOREUSE; |
488 | return sys_fadvise64(fd, offset, len, advise); | 488 | return sys_fadvise64(fd, offset, len, advise); |
489 | } | 489 | } |
490 | 490 | ||
491 | struct fadvise64_64_args { | 491 | struct fadvise64_64_args { |
492 | int fd; | 492 | int fd; |
493 | long long offset; | 493 | long long offset; |
494 | long long len; | 494 | long long len; |
495 | int advice; | 495 | int advice; |
496 | }; | 496 | }; |
497 | 497 | ||
498 | asmlinkage long | 498 | asmlinkage long |
499 | sys32_fadvise64_64(struct fadvise64_64_args __user *args) | 499 | sys32_fadvise64_64(struct fadvise64_64_args __user *args) |
500 | { | 500 | { |
501 | struct fadvise64_64_args a; | 501 | struct fadvise64_64_args a; |
502 | 502 | ||
503 | if ( copy_from_user(&a, args, sizeof(a)) ) | 503 | if ( copy_from_user(&a, args, sizeof(a)) ) |
504 | return -EFAULT; | 504 | return -EFAULT; |
505 | if (a.advice == 4) | 505 | if (a.advice == 4) |
506 | a.advice = POSIX_FADV_DONTNEED; | 506 | a.advice = POSIX_FADV_DONTNEED; |
507 | else if (a.advice == 5) | 507 | else if (a.advice == 5) |
508 | a.advice = POSIX_FADV_NOREUSE; | 508 | a.advice = POSIX_FADV_NOREUSE; |
509 | return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice); | 509 | return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice); |
510 | } | 510 | } |
511 | 511 |
include/linux/cred.h
1 | /* Credentials management - see Documentation/security/credentials.txt | 1 | /* Credentials management - see Documentation/security/credentials.txt |
2 | * | 2 | * |
3 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 4 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public Licence | 7 | * modify it under the terms of the GNU General Public Licence |
8 | * as published by the Free Software Foundation; either version | 8 | * as published by the Free Software Foundation; either version |
9 | * 2 of the Licence, or (at your option) any later version. | 9 | * 2 of the Licence, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #ifndef _LINUX_CRED_H | 12 | #ifndef _LINUX_CRED_H |
13 | #define _LINUX_CRED_H | 13 | #define _LINUX_CRED_H |
14 | 14 | ||
15 | #include <linux/capability.h> | 15 | #include <linux/capability.h> |
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/key.h> | 17 | #include <linux/key.h> |
18 | #include <linux/selinux.h> | 18 | #include <linux/selinux.h> |
19 | #include <linux/atomic.h> | 19 | #include <linux/atomic.h> |
20 | #include <linux/uidgid.h> | 20 | #include <linux/uidgid.h> |
21 | 21 | ||
22 | struct user_struct; | 22 | struct user_struct; |
23 | struct cred; | 23 | struct cred; |
24 | struct inode; | 24 | struct inode; |
25 | 25 | ||
26 | /* | 26 | /* |
27 | * COW Supplementary groups list | 27 | * COW Supplementary groups list |
28 | */ | 28 | */ |
29 | #define NGROUPS_SMALL 32 | 29 | #define NGROUPS_SMALL 32 |
30 | #define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(kgid_t))) | 30 | #define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(kgid_t))) |
31 | 31 | ||
32 | struct group_info { | 32 | struct group_info { |
33 | atomic_t usage; | 33 | atomic_t usage; |
34 | int ngroups; | 34 | int ngroups; |
35 | int nblocks; | 35 | int nblocks; |
36 | kgid_t small_block[NGROUPS_SMALL]; | 36 | kgid_t small_block[NGROUPS_SMALL]; |
37 | kgid_t *blocks[0]; | 37 | kgid_t *blocks[0]; |
38 | }; | 38 | }; |
39 | 39 | ||
40 | /** | 40 | /** |
41 | * get_group_info - Get a reference to a group info structure | 41 | * get_group_info - Get a reference to a group info structure |
42 | * @group_info: The group info to reference | 42 | * @group_info: The group info to reference |
43 | * | 43 | * |
44 | * This gets a reference to a set of supplementary groups. | 44 | * This gets a reference to a set of supplementary groups. |
45 | * | 45 | * |
46 | * If the caller is accessing a task's credentials, they must hold the RCU read | 46 | * If the caller is accessing a task's credentials, they must hold the RCU read |
47 | * lock when reading. | 47 | * lock when reading. |
48 | */ | 48 | */ |
49 | static inline struct group_info *get_group_info(struct group_info *gi) | 49 | static inline struct group_info *get_group_info(struct group_info *gi) |
50 | { | 50 | { |
51 | atomic_inc(&gi->usage); | 51 | atomic_inc(&gi->usage); |
52 | return gi; | 52 | return gi; |
53 | } | 53 | } |
54 | 54 | ||
55 | /** | 55 | /** |
56 | * put_group_info - Release a reference to a group info structure | 56 | * put_group_info - Release a reference to a group info structure |
57 | * @group_info: The group info to release | 57 | * @group_info: The group info to release |
58 | */ | 58 | */ |
59 | #define put_group_info(group_info) \ | 59 | #define put_group_info(group_info) \ |
60 | do { \ | 60 | do { \ |
61 | if (atomic_dec_and_test(&(group_info)->usage)) \ | 61 | if (atomic_dec_and_test(&(group_info)->usage)) \ |
62 | groups_free(group_info); \ | 62 | groups_free(group_info); \ |
63 | } while (0) | 63 | } while (0) |
64 | 64 | ||
65 | extern struct group_info *groups_alloc(int); | 65 | extern struct group_info *groups_alloc(int); |
66 | extern struct group_info init_groups; | 66 | extern struct group_info init_groups; |
67 | extern void groups_free(struct group_info *); | 67 | extern void groups_free(struct group_info *); |
68 | extern int set_current_groups(struct group_info *); | 68 | extern int set_current_groups(struct group_info *); |
69 | extern int set_groups(struct cred *, struct group_info *); | 69 | extern int set_groups(struct cred *, struct group_info *); |
70 | extern int groups_search(const struct group_info *, kgid_t); | 70 | extern int groups_search(const struct group_info *, kgid_t); |
71 | extern bool may_setgroups(void); | ||
71 | 72 | ||
72 | /* access the groups "array" with this macro */ | 73 | /* access the groups "array" with this macro */ |
73 | #define GROUP_AT(gi, i) \ | 74 | #define GROUP_AT(gi, i) \ |
74 | ((gi)->blocks[(i) / NGROUPS_PER_BLOCK][(i) % NGROUPS_PER_BLOCK]) | 75 | ((gi)->blocks[(i) / NGROUPS_PER_BLOCK][(i) % NGROUPS_PER_BLOCK]) |
75 | 76 | ||
76 | extern int in_group_p(kgid_t); | 77 | extern int in_group_p(kgid_t); |
77 | extern int in_egroup_p(kgid_t); | 78 | extern int in_egroup_p(kgid_t); |
78 | 79 | ||
79 | /* | 80 | /* |
80 | * The security context of a task | 81 | * The security context of a task |
81 | * | 82 | * |
82 | * The parts of the context break down into two categories: | 83 | * The parts of the context break down into two categories: |
83 | * | 84 | * |
84 | * (1) The objective context of a task. These parts are used when some other | 85 | * (1) The objective context of a task. These parts are used when some other |
85 | * task is attempting to affect this one. | 86 | * task is attempting to affect this one. |
86 | * | 87 | * |
87 | * (2) The subjective context. These details are used when the task is acting | 88 | * (2) The subjective context. These details are used when the task is acting |
88 | * upon another object, be that a file, a task, a key or whatever. | 89 | * upon another object, be that a file, a task, a key or whatever. |
89 | * | 90 | * |
90 | * Note that some members of this structure belong to both categories - the | 91 | * Note that some members of this structure belong to both categories - the |
91 | * LSM security pointer for instance. | 92 | * LSM security pointer for instance. |
92 | * | 93 | * |
93 | * A task has two security pointers. task->real_cred points to the objective | 94 | * A task has two security pointers. task->real_cred points to the objective |
94 | * context that defines that task's actual details. The objective part of this | 95 | * context that defines that task's actual details. The objective part of this |
95 | * context is used whenever that task is acted upon. | 96 | * context is used whenever that task is acted upon. |
96 | * | 97 | * |
97 | * task->cred points to the subjective context that defines the details of how | 98 | * task->cred points to the subjective context that defines the details of how |
98 | * that task is going to act upon another object. This may be overridden | 99 | * that task is going to act upon another object. This may be overridden |
99 | * temporarily to point to another security context, but normally points to the | 100 | * temporarily to point to another security context, but normally points to the |
100 | * same context as task->real_cred. | 101 | * same context as task->real_cred. |
101 | */ | 102 | */ |
102 | struct cred { | 103 | struct cred { |
103 | atomic_t usage; | 104 | atomic_t usage; |
104 | #ifdef CONFIG_DEBUG_CREDENTIALS | 105 | #ifdef CONFIG_DEBUG_CREDENTIALS |
105 | atomic_t subscribers; /* number of processes subscribed */ | 106 | atomic_t subscribers; /* number of processes subscribed */ |
106 | void *put_addr; | 107 | void *put_addr; |
107 | unsigned magic; | 108 | unsigned magic; |
108 | #define CRED_MAGIC 0x43736564 | 109 | #define CRED_MAGIC 0x43736564 |
109 | #define CRED_MAGIC_DEAD 0x44656144 | 110 | #define CRED_MAGIC_DEAD 0x44656144 |
110 | #endif | 111 | #endif |
111 | kuid_t uid; /* real UID of the task */ | 112 | kuid_t uid; /* real UID of the task */ |
112 | kgid_t gid; /* real GID of the task */ | 113 | kgid_t gid; /* real GID of the task */ |
113 | kuid_t suid; /* saved UID of the task */ | 114 | kuid_t suid; /* saved UID of the task */ |
114 | kgid_t sgid; /* saved GID of the task */ | 115 | kgid_t sgid; /* saved GID of the task */ |
115 | kuid_t euid; /* effective UID of the task */ | 116 | kuid_t euid; /* effective UID of the task */ |
116 | kgid_t egid; /* effective GID of the task */ | 117 | kgid_t egid; /* effective GID of the task */ |
117 | kuid_t fsuid; /* UID for VFS ops */ | 118 | kuid_t fsuid; /* UID for VFS ops */ |
118 | kgid_t fsgid; /* GID for VFS ops */ | 119 | kgid_t fsgid; /* GID for VFS ops */ |
119 | unsigned securebits; /* SUID-less security management */ | 120 | unsigned securebits; /* SUID-less security management */ |
120 | kernel_cap_t cap_inheritable; /* caps our children can inherit */ | 121 | kernel_cap_t cap_inheritable; /* caps our children can inherit */ |
121 | kernel_cap_t cap_permitted; /* caps we're permitted */ | 122 | kernel_cap_t cap_permitted; /* caps we're permitted */ |
122 | kernel_cap_t cap_effective; /* caps we can actually use */ | 123 | kernel_cap_t cap_effective; /* caps we can actually use */ |
123 | kernel_cap_t cap_bset; /* capability bounding set */ | 124 | kernel_cap_t cap_bset; /* capability bounding set */ |
124 | #ifdef CONFIG_KEYS | 125 | #ifdef CONFIG_KEYS |
125 | unsigned char jit_keyring; /* default keyring to attach requested | 126 | unsigned char jit_keyring; /* default keyring to attach requested |
126 | * keys to */ | 127 | * keys to */ |
127 | struct key __rcu *session_keyring; /* keyring inherited over fork */ | 128 | struct key __rcu *session_keyring; /* keyring inherited over fork */ |
128 | struct key *process_keyring; /* keyring private to this process */ | 129 | struct key *process_keyring; /* keyring private to this process */ |
129 | struct key *thread_keyring; /* keyring private to this thread */ | 130 | struct key *thread_keyring; /* keyring private to this thread */ |
130 | struct key *request_key_auth; /* assumed request_key authority */ | 131 | struct key *request_key_auth; /* assumed request_key authority */ |
131 | #endif | 132 | #endif |
132 | #ifdef CONFIG_SECURITY | 133 | #ifdef CONFIG_SECURITY |
133 | void *security; /* subjective LSM security */ | 134 | void *security; /* subjective LSM security */ |
134 | #endif | 135 | #endif |
135 | struct user_struct *user; /* real user ID subscription */ | 136 | struct user_struct *user; /* real user ID subscription */ |
136 | struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ | 137 | struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ |
137 | struct group_info *group_info; /* supplementary groups for euid/fsgid */ | 138 | struct group_info *group_info; /* supplementary groups for euid/fsgid */ |
138 | struct rcu_head rcu; /* RCU deletion hook */ | 139 | struct rcu_head rcu; /* RCU deletion hook */ |
139 | }; | 140 | }; |
140 | 141 | ||
141 | extern void __put_cred(struct cred *); | 142 | extern void __put_cred(struct cred *); |
142 | extern void exit_creds(struct task_struct *); | 143 | extern void exit_creds(struct task_struct *); |
143 | extern int copy_creds(struct task_struct *, unsigned long); | 144 | extern int copy_creds(struct task_struct *, unsigned long); |
144 | extern const struct cred *get_task_cred(struct task_struct *); | 145 | extern const struct cred *get_task_cred(struct task_struct *); |
145 | extern struct cred *cred_alloc_blank(void); | 146 | extern struct cred *cred_alloc_blank(void); |
146 | extern struct cred *prepare_creds(void); | 147 | extern struct cred *prepare_creds(void); |
147 | extern struct cred *prepare_exec_creds(void); | 148 | extern struct cred *prepare_exec_creds(void); |
148 | extern int commit_creds(struct cred *); | 149 | extern int commit_creds(struct cred *); |
149 | extern void abort_creds(struct cred *); | 150 | extern void abort_creds(struct cred *); |
150 | extern const struct cred *override_creds(const struct cred *); | 151 | extern const struct cred *override_creds(const struct cred *); |
151 | extern void revert_creds(const struct cred *); | 152 | extern void revert_creds(const struct cred *); |
152 | extern struct cred *prepare_kernel_cred(struct task_struct *); | 153 | extern struct cred *prepare_kernel_cred(struct task_struct *); |
153 | extern int change_create_files_as(struct cred *, struct inode *); | 154 | extern int change_create_files_as(struct cred *, struct inode *); |
154 | extern int set_security_override(struct cred *, u32); | 155 | extern int set_security_override(struct cred *, u32); |
155 | extern int set_security_override_from_ctx(struct cred *, const char *); | 156 | extern int set_security_override_from_ctx(struct cred *, const char *); |
156 | extern int set_create_files_as(struct cred *, struct inode *); | 157 | extern int set_create_files_as(struct cred *, struct inode *); |
157 | extern void __init cred_init(void); | 158 | extern void __init cred_init(void); |
158 | 159 | ||
159 | /* | 160 | /* |
160 | * check for validity of credentials | 161 | * check for validity of credentials |
161 | */ | 162 | */ |
162 | #ifdef CONFIG_DEBUG_CREDENTIALS | 163 | #ifdef CONFIG_DEBUG_CREDENTIALS |
163 | extern void __invalid_creds(const struct cred *, const char *, unsigned); | 164 | extern void __invalid_creds(const struct cred *, const char *, unsigned); |
164 | extern void __validate_process_creds(struct task_struct *, | 165 | extern void __validate_process_creds(struct task_struct *, |
165 | const char *, unsigned); | 166 | const char *, unsigned); |
166 | 167 | ||
167 | extern bool creds_are_invalid(const struct cred *cred); | 168 | extern bool creds_are_invalid(const struct cred *cred); |
168 | 169 | ||
169 | static inline void __validate_creds(const struct cred *cred, | 170 | static inline void __validate_creds(const struct cred *cred, |
170 | const char *file, unsigned line) | 171 | const char *file, unsigned line) |
171 | { | 172 | { |
172 | if (unlikely(creds_are_invalid(cred))) | 173 | if (unlikely(creds_are_invalid(cred))) |
173 | __invalid_creds(cred, file, line); | 174 | __invalid_creds(cred, file, line); |
174 | } | 175 | } |
175 | 176 | ||
176 | #define validate_creds(cred) \ | 177 | #define validate_creds(cred) \ |
177 | do { \ | 178 | do { \ |
178 | __validate_creds((cred), __FILE__, __LINE__); \ | 179 | __validate_creds((cred), __FILE__, __LINE__); \ |
179 | } while(0) | 180 | } while(0) |
180 | 181 | ||
181 | #define validate_process_creds() \ | 182 | #define validate_process_creds() \ |
182 | do { \ | 183 | do { \ |
183 | __validate_process_creds(current, __FILE__, __LINE__); \ | 184 | __validate_process_creds(current, __FILE__, __LINE__); \ |
184 | } while(0) | 185 | } while(0) |
185 | 186 | ||
186 | extern void validate_creds_for_do_exit(struct task_struct *); | 187 | extern void validate_creds_for_do_exit(struct task_struct *); |
187 | #else | 188 | #else |
188 | static inline void validate_creds(const struct cred *cred) | 189 | static inline void validate_creds(const struct cred *cred) |
189 | { | 190 | { |
190 | } | 191 | } |
191 | static inline void validate_creds_for_do_exit(struct task_struct *tsk) | 192 | static inline void validate_creds_for_do_exit(struct task_struct *tsk) |
192 | { | 193 | { |
193 | } | 194 | } |
194 | static inline void validate_process_creds(void) | 195 | static inline void validate_process_creds(void) |
195 | { | 196 | { |
196 | } | 197 | } |
197 | #endif | 198 | #endif |
198 | 199 | ||
199 | /** | 200 | /** |
200 | * get_new_cred - Get a reference on a new set of credentials | 201 | * get_new_cred - Get a reference on a new set of credentials |
201 | * @cred: The new credentials to reference | 202 | * @cred: The new credentials to reference |
202 | * | 203 | * |
203 | * Get a reference on the specified set of new credentials. The caller must | 204 | * Get a reference on the specified set of new credentials. The caller must |
204 | * release the reference. | 205 | * release the reference. |
205 | */ | 206 | */ |
206 | static inline struct cred *get_new_cred(struct cred *cred) | 207 | static inline struct cred *get_new_cred(struct cred *cred) |
207 | { | 208 | { |
208 | atomic_inc(&cred->usage); | 209 | atomic_inc(&cred->usage); |
209 | return cred; | 210 | return cred; |
210 | } | 211 | } |
211 | 212 | ||
212 | /** | 213 | /** |
213 | * get_cred - Get a reference on a set of credentials | 214 | * get_cred - Get a reference on a set of credentials |
214 | * @cred: The credentials to reference | 215 | * @cred: The credentials to reference |
215 | * | 216 | * |
216 | * Get a reference on the specified set of credentials. The caller must | 217 | * Get a reference on the specified set of credentials. The caller must |
217 | * release the reference. | 218 | * release the reference. |
218 | * | 219 | * |
219 | * This is used to deal with a committed set of credentials. Although the | 220 | * This is used to deal with a committed set of credentials. Although the |
220 | * pointer is const, this will temporarily discard the const and increment the | 221 | * pointer is const, this will temporarily discard the const and increment the |
221 | * usage count. The purpose of this is to attempt to catch at compile time the | 222 | * usage count. The purpose of this is to attempt to catch at compile time the |
222 | * accidental alteration of a set of credentials that should be considered | 223 | * accidental alteration of a set of credentials that should be considered |
223 | * immutable. | 224 | * immutable. |
224 | */ | 225 | */ |
225 | static inline const struct cred *get_cred(const struct cred *cred) | 226 | static inline const struct cred *get_cred(const struct cred *cred) |
226 | { | 227 | { |
227 | struct cred *nonconst_cred = (struct cred *) cred; | 228 | struct cred *nonconst_cred = (struct cred *) cred; |
228 | validate_creds(cred); | 229 | validate_creds(cred); |
229 | return get_new_cred(nonconst_cred); | 230 | return get_new_cred(nonconst_cred); |
230 | } | 231 | } |
231 | 232 | ||
232 | /** | 233 | /** |
233 | * put_cred - Release a reference to a set of credentials | 234 | * put_cred - Release a reference to a set of credentials |
234 | * @cred: The credentials to release | 235 | * @cred: The credentials to release |
235 | * | 236 | * |
236 | * Release a reference to a set of credentials, deleting them when the last ref | 237 | * Release a reference to a set of credentials, deleting them when the last ref |
237 | * is released. | 238 | * is released. |
238 | * | 239 | * |
239 | * This takes a const pointer to a set of credentials because the credentials | 240 | * This takes a const pointer to a set of credentials because the credentials |
240 | * on task_struct are attached by const pointers to prevent accidental | 241 | * on task_struct are attached by const pointers to prevent accidental |
241 | * alteration of otherwise immutable credential sets. | 242 | * alteration of otherwise immutable credential sets. |
242 | */ | 243 | */ |
243 | static inline void put_cred(const struct cred *_cred) | 244 | static inline void put_cred(const struct cred *_cred) |
244 | { | 245 | { |
245 | struct cred *cred = (struct cred *) _cred; | 246 | struct cred *cred = (struct cred *) _cred; |
246 | 247 | ||
247 | validate_creds(cred); | 248 | validate_creds(cred); |
248 | if (atomic_dec_and_test(&(cred)->usage)) | 249 | if (atomic_dec_and_test(&(cred)->usage)) |
249 | __put_cred(cred); | 250 | __put_cred(cred); |
250 | } | 251 | } |
251 | 252 | ||
252 | /** | 253 | /** |
253 | * current_cred - Access the current task's subjective credentials | 254 | * current_cred - Access the current task's subjective credentials |
254 | * | 255 | * |
255 | * Access the subjective credentials of the current task. RCU-safe, | 256 | * Access the subjective credentials of the current task. RCU-safe, |
256 | * since nobody else can modify it. | 257 | * since nobody else can modify it. |
257 | */ | 258 | */ |
258 | #define current_cred() \ | 259 | #define current_cred() \ |
259 | rcu_dereference_protected(current->cred, 1) | 260 | rcu_dereference_protected(current->cred, 1) |
260 | 261 | ||
261 | /** | 262 | /** |
262 | * __task_cred - Access a task's objective credentials | 263 | * __task_cred - Access a task's objective credentials |
263 | * @task: The task to query | 264 | * @task: The task to query |
264 | * | 265 | * |
265 | * Access the objective credentials of a task. The caller must hold the RCU | 266 | * Access the objective credentials of a task. The caller must hold the RCU |
266 | * readlock. | 267 | * readlock. |
267 | * | 268 | * |
268 | * The result of this function should not be passed directly to get_cred(); | 269 | * The result of this function should not be passed directly to get_cred(); |
269 | * rather get_task_cred() should be used instead. | 270 | * rather get_task_cred() should be used instead. |
270 | */ | 271 | */ |
271 | #define __task_cred(task) \ | 272 | #define __task_cred(task) \ |
272 | rcu_dereference((task)->real_cred) | 273 | rcu_dereference((task)->real_cred) |
273 | 274 | ||
274 | /** | 275 | /** |
275 | * get_current_cred - Get the current task's subjective credentials | 276 | * get_current_cred - Get the current task's subjective credentials |
276 | * | 277 | * |
277 | * Get the subjective credentials of the current task, pinning them so that | 278 | * Get the subjective credentials of the current task, pinning them so that |
278 | * they can't go away. Accessing the current task's credentials directly is | 279 | * they can't go away. Accessing the current task's credentials directly is |
279 | * not permitted. | 280 | * not permitted. |
280 | */ | 281 | */ |
281 | #define get_current_cred() \ | 282 | #define get_current_cred() \ |
282 | (get_cred(current_cred())) | 283 | (get_cred(current_cred())) |
283 | 284 | ||
284 | /** | 285 | /** |
285 | * get_current_user - Get the current task's user_struct | 286 | * get_current_user - Get the current task's user_struct |
286 | * | 287 | * |
287 | * Get the user record of the current task, pinning it so that it can't go | 288 | * Get the user record of the current task, pinning it so that it can't go |
288 | * away. | 289 | * away. |
289 | */ | 290 | */ |
290 | #define get_current_user() \ | 291 | #define get_current_user() \ |
291 | ({ \ | 292 | ({ \ |
292 | struct user_struct *__u; \ | 293 | struct user_struct *__u; \ |
293 | const struct cred *__cred; \ | 294 | const struct cred *__cred; \ |
294 | __cred = current_cred(); \ | 295 | __cred = current_cred(); \ |
295 | __u = get_uid(__cred->user); \ | 296 | __u = get_uid(__cred->user); \ |
296 | __u; \ | 297 | __u; \ |
297 | }) | 298 | }) |
298 | 299 | ||
299 | /** | 300 | /** |
300 | * get_current_groups - Get the current task's supplementary group list | 301 | * get_current_groups - Get the current task's supplementary group list |
301 | * | 302 | * |
302 | * Get the supplementary group list of the current task, pinning it so that it | 303 | * Get the supplementary group list of the current task, pinning it so that it |
303 | * can't go away. | 304 | * can't go away. |
304 | */ | 305 | */ |
305 | #define get_current_groups() \ | 306 | #define get_current_groups() \ |
306 | ({ \ | 307 | ({ \ |
307 | struct group_info *__groups; \ | 308 | struct group_info *__groups; \ |
308 | const struct cred *__cred; \ | 309 | const struct cred *__cred; \ |
309 | __cred = current_cred(); \ | 310 | __cred = current_cred(); \ |
310 | __groups = get_group_info(__cred->group_info); \ | 311 | __groups = get_group_info(__cred->group_info); \ |
311 | __groups; \ | 312 | __groups; \ |
312 | }) | 313 | }) |
313 | 314 | ||
314 | #define task_cred_xxx(task, xxx) \ | 315 | #define task_cred_xxx(task, xxx) \ |
315 | ({ \ | 316 | ({ \ |
316 | __typeof__(((struct cred *)NULL)->xxx) ___val; \ | 317 | __typeof__(((struct cred *)NULL)->xxx) ___val; \ |
317 | rcu_read_lock(); \ | 318 | rcu_read_lock(); \ |
318 | ___val = __task_cred((task))->xxx; \ | 319 | ___val = __task_cred((task))->xxx; \ |
319 | rcu_read_unlock(); \ | 320 | rcu_read_unlock(); \ |
320 | ___val; \ | 321 | ___val; \ |
321 | }) | 322 | }) |
322 | 323 | ||
323 | #define task_uid(task) (task_cred_xxx((task), uid)) | 324 | #define task_uid(task) (task_cred_xxx((task), uid)) |
324 | #define task_euid(task) (task_cred_xxx((task), euid)) | 325 | #define task_euid(task) (task_cred_xxx((task), euid)) |
325 | 326 | ||
326 | #define current_cred_xxx(xxx) \ | 327 | #define current_cred_xxx(xxx) \ |
327 | ({ \ | 328 | ({ \ |
328 | current_cred()->xxx; \ | 329 | current_cred()->xxx; \ |
329 | }) | 330 | }) |
330 | 331 | ||
331 | #define current_uid() (current_cred_xxx(uid)) | 332 | #define current_uid() (current_cred_xxx(uid)) |
332 | #define current_gid() (current_cred_xxx(gid)) | 333 | #define current_gid() (current_cred_xxx(gid)) |
333 | #define current_euid() (current_cred_xxx(euid)) | 334 | #define current_euid() (current_cred_xxx(euid)) |
334 | #define current_egid() (current_cred_xxx(egid)) | 335 | #define current_egid() (current_cred_xxx(egid)) |
335 | #define current_suid() (current_cred_xxx(suid)) | 336 | #define current_suid() (current_cred_xxx(suid)) |
336 | #define current_sgid() (current_cred_xxx(sgid)) | 337 | #define current_sgid() (current_cred_xxx(sgid)) |
337 | #define current_fsuid() (current_cred_xxx(fsuid)) | 338 | #define current_fsuid() (current_cred_xxx(fsuid)) |
338 | #define current_fsgid() (current_cred_xxx(fsgid)) | 339 | #define current_fsgid() (current_cred_xxx(fsgid)) |
339 | #define current_cap() (current_cred_xxx(cap_effective)) | 340 | #define current_cap() (current_cred_xxx(cap_effective)) |
340 | #define current_user() (current_cred_xxx(user)) | 341 | #define current_user() (current_cred_xxx(user)) |
341 | #define current_security() (current_cred_xxx(security)) | 342 | #define current_security() (current_cred_xxx(security)) |
342 | 343 | ||
343 | extern struct user_namespace init_user_ns; | 344 | extern struct user_namespace init_user_ns; |
344 | #ifdef CONFIG_USER_NS | 345 | #ifdef CONFIG_USER_NS |
345 | #define current_user_ns() (current_cred_xxx(user_ns)) | 346 | #define current_user_ns() (current_cred_xxx(user_ns)) |
346 | #else | 347 | #else |
347 | #define current_user_ns() (&init_user_ns) | 348 | #define current_user_ns() (&init_user_ns) |
348 | #endif | 349 | #endif |
349 | 350 | ||
350 | 351 | ||
351 | #define current_uid_gid(_uid, _gid) \ | 352 | #define current_uid_gid(_uid, _gid) \ |
352 | do { \ | 353 | do { \ |
353 | const struct cred *__cred; \ | 354 | const struct cred *__cred; \ |
354 | __cred = current_cred(); \ | 355 | __cred = current_cred(); \ |
355 | *(_uid) = __cred->uid; \ | 356 | *(_uid) = __cred->uid; \ |
356 | *(_gid) = __cred->gid; \ | 357 | *(_gid) = __cred->gid; \ |
357 | } while(0) | 358 | } while(0) |
358 | 359 | ||
359 | #define current_euid_egid(_euid, _egid) \ | 360 | #define current_euid_egid(_euid, _egid) \ |
360 | do { \ | 361 | do { \ |
361 | const struct cred *__cred; \ | 362 | const struct cred *__cred; \ |
362 | __cred = current_cred(); \ | 363 | __cred = current_cred(); \ |
363 | *(_euid) = __cred->euid; \ | 364 | *(_euid) = __cred->euid; \ |
364 | *(_egid) = __cred->egid; \ | 365 | *(_egid) = __cred->egid; \ |
365 | } while(0) | 366 | } while(0) |
366 | 367 | ||
367 | #define current_fsuid_fsgid(_fsuid, _fsgid) \ | 368 | #define current_fsuid_fsgid(_fsuid, _fsgid) \ |
368 | do { \ | 369 | do { \ |
369 | const struct cred *__cred; \ | 370 | const struct cred *__cred; \ |
370 | __cred = current_cred(); \ | 371 | __cred = current_cred(); \ |
371 | *(_fsuid) = __cred->fsuid; \ | 372 | *(_fsuid) = __cred->fsuid; \ |
372 | *(_fsgid) = __cred->fsgid; \ | 373 | *(_fsgid) = __cred->fsgid; \ |
373 | } while(0) | 374 | } while(0) |
374 | 375 | ||
375 | #endif /* _LINUX_CRED_H */ | 376 | #endif /* _LINUX_CRED_H */ |
376 | 377 |
kernel/groups.c
1 | /* | 1 | /* |
2 | * Supplementary group IDs | 2 | * Supplementary group IDs |
3 | */ | 3 | */ |
4 | #include <linux/cred.h> | 4 | #include <linux/cred.h> |
5 | #include <linux/export.h> | 5 | #include <linux/export.h> |
6 | #include <linux/slab.h> | 6 | #include <linux/slab.h> |
7 | #include <linux/security.h> | 7 | #include <linux/security.h> |
8 | #include <linux/syscalls.h> | 8 | #include <linux/syscalls.h> |
9 | #include <asm/uaccess.h> | 9 | #include <asm/uaccess.h> |
10 | 10 | ||
11 | /* init to 2 - one for init_task, one to ensure it is never freed */ | 11 | /* init to 2 - one for init_task, one to ensure it is never freed */ |
12 | struct group_info init_groups = { .usage = ATOMIC_INIT(2) }; | 12 | struct group_info init_groups = { .usage = ATOMIC_INIT(2) }; |
13 | 13 | ||
14 | struct group_info *groups_alloc(int gidsetsize) | 14 | struct group_info *groups_alloc(int gidsetsize) |
15 | { | 15 | { |
16 | struct group_info *group_info; | 16 | struct group_info *group_info; |
17 | int nblocks; | 17 | int nblocks; |
18 | int i; | 18 | int i; |
19 | 19 | ||
20 | nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK; | 20 | nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK; |
21 | /* Make sure we always allocate at least one indirect block pointer */ | 21 | /* Make sure we always allocate at least one indirect block pointer */ |
22 | nblocks = nblocks ? : 1; | 22 | nblocks = nblocks ? : 1; |
23 | group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER); | 23 | group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER); |
24 | if (!group_info) | 24 | if (!group_info) |
25 | return NULL; | 25 | return NULL; |
26 | group_info->ngroups = gidsetsize; | 26 | group_info->ngroups = gidsetsize; |
27 | group_info->nblocks = nblocks; | 27 | group_info->nblocks = nblocks; |
28 | atomic_set(&group_info->usage, 1); | 28 | atomic_set(&group_info->usage, 1); |
29 | 29 | ||
30 | if (gidsetsize <= NGROUPS_SMALL) | 30 | if (gidsetsize <= NGROUPS_SMALL) |
31 | group_info->blocks[0] = group_info->small_block; | 31 | group_info->blocks[0] = group_info->small_block; |
32 | else { | 32 | else { |
33 | for (i = 0; i < nblocks; i++) { | 33 | for (i = 0; i < nblocks; i++) { |
34 | kgid_t *b; | 34 | kgid_t *b; |
35 | b = (void *)__get_free_page(GFP_USER); | 35 | b = (void *)__get_free_page(GFP_USER); |
36 | if (!b) | 36 | if (!b) |
37 | goto out_undo_partial_alloc; | 37 | goto out_undo_partial_alloc; |
38 | group_info->blocks[i] = b; | 38 | group_info->blocks[i] = b; |
39 | } | 39 | } |
40 | } | 40 | } |
41 | return group_info; | 41 | return group_info; |
42 | 42 | ||
43 | out_undo_partial_alloc: | 43 | out_undo_partial_alloc: |
44 | while (--i >= 0) { | 44 | while (--i >= 0) { |
45 | free_page((unsigned long)group_info->blocks[i]); | 45 | free_page((unsigned long)group_info->blocks[i]); |
46 | } | 46 | } |
47 | kfree(group_info); | 47 | kfree(group_info); |
48 | return NULL; | 48 | return NULL; |
49 | } | 49 | } |
50 | 50 | ||
51 | EXPORT_SYMBOL(groups_alloc); | 51 | EXPORT_SYMBOL(groups_alloc); |
52 | 52 | ||
53 | void groups_free(struct group_info *group_info) | 53 | void groups_free(struct group_info *group_info) |
54 | { | 54 | { |
55 | if (group_info->blocks[0] != group_info->small_block) { | 55 | if (group_info->blocks[0] != group_info->small_block) { |
56 | int i; | 56 | int i; |
57 | for (i = 0; i < group_info->nblocks; i++) | 57 | for (i = 0; i < group_info->nblocks; i++) |
58 | free_page((unsigned long)group_info->blocks[i]); | 58 | free_page((unsigned long)group_info->blocks[i]); |
59 | } | 59 | } |
60 | kfree(group_info); | 60 | kfree(group_info); |
61 | } | 61 | } |
62 | 62 | ||
63 | EXPORT_SYMBOL(groups_free); | 63 | EXPORT_SYMBOL(groups_free); |
64 | 64 | ||
65 | /* export the group_info to a user-space array */ | 65 | /* export the group_info to a user-space array */ |
66 | static int groups_to_user(gid_t __user *grouplist, | 66 | static int groups_to_user(gid_t __user *grouplist, |
67 | const struct group_info *group_info) | 67 | const struct group_info *group_info) |
68 | { | 68 | { |
69 | struct user_namespace *user_ns = current_user_ns(); | 69 | struct user_namespace *user_ns = current_user_ns(); |
70 | int i; | 70 | int i; |
71 | unsigned int count = group_info->ngroups; | 71 | unsigned int count = group_info->ngroups; |
72 | 72 | ||
73 | for (i = 0; i < count; i++) { | 73 | for (i = 0; i < count; i++) { |
74 | gid_t gid; | 74 | gid_t gid; |
75 | gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i)); | 75 | gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i)); |
76 | if (put_user(gid, grouplist+i)) | 76 | if (put_user(gid, grouplist+i)) |
77 | return -EFAULT; | 77 | return -EFAULT; |
78 | } | 78 | } |
79 | return 0; | 79 | return 0; |
80 | } | 80 | } |
81 | 81 | ||
82 | /* fill a group_info from a user-space array - it must be allocated already */ | 82 | /* fill a group_info from a user-space array - it must be allocated already */ |
83 | static int groups_from_user(struct group_info *group_info, | 83 | static int groups_from_user(struct group_info *group_info, |
84 | gid_t __user *grouplist) | 84 | gid_t __user *grouplist) |
85 | { | 85 | { |
86 | struct user_namespace *user_ns = current_user_ns(); | 86 | struct user_namespace *user_ns = current_user_ns(); |
87 | int i; | 87 | int i; |
88 | unsigned int count = group_info->ngroups; | 88 | unsigned int count = group_info->ngroups; |
89 | 89 | ||
90 | for (i = 0; i < count; i++) { | 90 | for (i = 0; i < count; i++) { |
91 | gid_t gid; | 91 | gid_t gid; |
92 | kgid_t kgid; | 92 | kgid_t kgid; |
93 | if (get_user(gid, grouplist+i)) | 93 | if (get_user(gid, grouplist+i)) |
94 | return -EFAULT; | 94 | return -EFAULT; |
95 | 95 | ||
96 | kgid = make_kgid(user_ns, gid); | 96 | kgid = make_kgid(user_ns, gid); |
97 | if (!gid_valid(kgid)) | 97 | if (!gid_valid(kgid)) |
98 | return -EINVAL; | 98 | return -EINVAL; |
99 | 99 | ||
100 | GROUP_AT(group_info, i) = kgid; | 100 | GROUP_AT(group_info, i) = kgid; |
101 | } | 101 | } |
102 | return 0; | 102 | return 0; |
103 | } | 103 | } |
104 | 104 | ||
105 | /* a simple Shell sort */ | 105 | /* a simple Shell sort */ |
106 | static void groups_sort(struct group_info *group_info) | 106 | static void groups_sort(struct group_info *group_info) |
107 | { | 107 | { |
108 | int base, max, stride; | 108 | int base, max, stride; |
109 | int gidsetsize = group_info->ngroups; | 109 | int gidsetsize = group_info->ngroups; |
110 | 110 | ||
111 | for (stride = 1; stride < gidsetsize; stride = 3 * stride + 1) | 111 | for (stride = 1; stride < gidsetsize; stride = 3 * stride + 1) |
112 | ; /* nothing */ | 112 | ; /* nothing */ |
113 | stride /= 3; | 113 | stride /= 3; |
114 | 114 | ||
115 | while (stride) { | 115 | while (stride) { |
116 | max = gidsetsize - stride; | 116 | max = gidsetsize - stride; |
117 | for (base = 0; base < max; base++) { | 117 | for (base = 0; base < max; base++) { |
118 | int left = base; | 118 | int left = base; |
119 | int right = left + stride; | 119 | int right = left + stride; |
120 | kgid_t tmp = GROUP_AT(group_info, right); | 120 | kgid_t tmp = GROUP_AT(group_info, right); |
121 | 121 | ||
122 | while (left >= 0 && gid_gt(GROUP_AT(group_info, left), tmp)) { | 122 | while (left >= 0 && gid_gt(GROUP_AT(group_info, left), tmp)) { |
123 | GROUP_AT(group_info, right) = | 123 | GROUP_AT(group_info, right) = |
124 | GROUP_AT(group_info, left); | 124 | GROUP_AT(group_info, left); |
125 | right = left; | 125 | right = left; |
126 | left -= stride; | 126 | left -= stride; |
127 | } | 127 | } |
128 | GROUP_AT(group_info, right) = tmp; | 128 | GROUP_AT(group_info, right) = tmp; |
129 | } | 129 | } |
130 | stride /= 3; | 130 | stride /= 3; |
131 | } | 131 | } |
132 | } | 132 | } |
133 | 133 | ||
134 | /* a simple bsearch */ | 134 | /* a simple bsearch */ |
135 | int groups_search(const struct group_info *group_info, kgid_t grp) | 135 | int groups_search(const struct group_info *group_info, kgid_t grp) |
136 | { | 136 | { |
137 | unsigned int left, right; | 137 | unsigned int left, right; |
138 | 138 | ||
139 | if (!group_info) | 139 | if (!group_info) |
140 | return 0; | 140 | return 0; |
141 | 141 | ||
142 | left = 0; | 142 | left = 0; |
143 | right = group_info->ngroups; | 143 | right = group_info->ngroups; |
144 | while (left < right) { | 144 | while (left < right) { |
145 | unsigned int mid = (left+right)/2; | 145 | unsigned int mid = (left+right)/2; |
146 | if (gid_gt(grp, GROUP_AT(group_info, mid))) | 146 | if (gid_gt(grp, GROUP_AT(group_info, mid))) |
147 | left = mid + 1; | 147 | left = mid + 1; |
148 | else if (gid_lt(grp, GROUP_AT(group_info, mid))) | 148 | else if (gid_lt(grp, GROUP_AT(group_info, mid))) |
149 | right = mid; | 149 | right = mid; |
150 | else | 150 | else |
151 | return 1; | 151 | return 1; |
152 | } | 152 | } |
153 | return 0; | 153 | return 0; |
154 | } | 154 | } |
155 | 155 | ||
156 | /** | 156 | /** |
157 | * set_groups - Change a group subscription in a set of credentials | 157 | * set_groups - Change a group subscription in a set of credentials |
158 | * @new: The newly prepared set of credentials to alter | 158 | * @new: The newly prepared set of credentials to alter |
159 | * @group_info: The group list to install | 159 | * @group_info: The group list to install |
160 | * | 160 | * |
161 | * Validate a group subscription and, if valid, insert it into a set | 161 | * Validate a group subscription and, if valid, insert it into a set |
162 | * of credentials. | 162 | * of credentials. |
163 | */ | 163 | */ |
164 | int set_groups(struct cred *new, struct group_info *group_info) | 164 | int set_groups(struct cred *new, struct group_info *group_info) |
165 | { | 165 | { |
166 | put_group_info(new->group_info); | 166 | put_group_info(new->group_info); |
167 | groups_sort(group_info); | 167 | groups_sort(group_info); |
168 | get_group_info(group_info); | 168 | get_group_info(group_info); |
169 | new->group_info = group_info; | 169 | new->group_info = group_info; |
170 | return 0; | 170 | return 0; |
171 | } | 171 | } |
172 | 172 | ||
173 | EXPORT_SYMBOL(set_groups); | 173 | EXPORT_SYMBOL(set_groups); |
174 | 174 | ||
175 | /** | 175 | /** |
176 | * set_current_groups - Change current's group subscription | 176 | * set_current_groups - Change current's group subscription |
177 | * @group_info: The group list to impose | 177 | * @group_info: The group list to impose |
178 | * | 178 | * |
179 | * Validate a group subscription and, if valid, impose it upon current's task | 179 | * Validate a group subscription and, if valid, impose it upon current's task |
180 | * security record. | 180 | * security record. |
181 | */ | 181 | */ |
182 | int set_current_groups(struct group_info *group_info) | 182 | int set_current_groups(struct group_info *group_info) |
183 | { | 183 | { |
184 | struct cred *new; | 184 | struct cred *new; |
185 | int ret; | 185 | int ret; |
186 | 186 | ||
187 | new = prepare_creds(); | 187 | new = prepare_creds(); |
188 | if (!new) | 188 | if (!new) |
189 | return -ENOMEM; | 189 | return -ENOMEM; |
190 | 190 | ||
191 | ret = set_groups(new, group_info); | 191 | ret = set_groups(new, group_info); |
192 | if (ret < 0) { | 192 | if (ret < 0) { |
193 | abort_creds(new); | 193 | abort_creds(new); |
194 | return ret; | 194 | return ret; |
195 | } | 195 | } |
196 | 196 | ||
197 | return commit_creds(new); | 197 | return commit_creds(new); |
198 | } | 198 | } |
199 | 199 | ||
200 | EXPORT_SYMBOL(set_current_groups); | 200 | EXPORT_SYMBOL(set_current_groups); |
201 | 201 | ||
202 | SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist) | 202 | SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist) |
203 | { | 203 | { |
204 | const struct cred *cred = current_cred(); | 204 | const struct cred *cred = current_cred(); |
205 | int i; | 205 | int i; |
206 | 206 | ||
207 | if (gidsetsize < 0) | 207 | if (gidsetsize < 0) |
208 | return -EINVAL; | 208 | return -EINVAL; |
209 | 209 | ||
210 | /* no need to grab task_lock here; it cannot change */ | 210 | /* no need to grab task_lock here; it cannot change */ |
211 | i = cred->group_info->ngroups; | 211 | i = cred->group_info->ngroups; |
212 | if (gidsetsize) { | 212 | if (gidsetsize) { |
213 | if (i > gidsetsize) { | 213 | if (i > gidsetsize) { |
214 | i = -EINVAL; | 214 | i = -EINVAL; |
215 | goto out; | 215 | goto out; |
216 | } | 216 | } |
217 | if (groups_to_user(grouplist, cred->group_info)) { | 217 | if (groups_to_user(grouplist, cred->group_info)) { |
218 | i = -EFAULT; | 218 | i = -EFAULT; |
219 | goto out; | 219 | goto out; |
220 | } | 220 | } |
221 | } | 221 | } |
222 | out: | 222 | out: |
223 | return i; | 223 | return i; |
224 | } | 224 | } |
225 | 225 | ||
226 | bool may_setgroups(void) | ||
227 | { | ||
228 | struct user_namespace *user_ns = current_user_ns(); | ||
229 | |||
230 | return ns_capable(user_ns, CAP_SETGID); | ||
231 | } | ||
232 | |||
226 | /* | 233 | /* |
227 | * SMP: Our groups are copy-on-write. We can set them safely | 234 | * SMP: Our groups are copy-on-write. We can set them safely |
228 | * without another task interfering. | 235 | * without another task interfering. |
229 | */ | 236 | */ |
230 | 237 | ||
231 | SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist) | 238 | SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist) |
232 | { | 239 | { |
233 | struct group_info *group_info; | 240 | struct group_info *group_info; |
234 | int retval; | 241 | int retval; |
235 | 242 | ||
236 | if (!ns_capable(current_user_ns(), CAP_SETGID)) | 243 | if (!may_setgroups()) |
237 | return -EPERM; | 244 | return -EPERM; |
238 | if ((unsigned)gidsetsize > NGROUPS_MAX) | 245 | if ((unsigned)gidsetsize > NGROUPS_MAX) |
239 | return -EINVAL; | 246 | return -EINVAL; |
240 | 247 | ||
241 | group_info = groups_alloc(gidsetsize); | 248 | group_info = groups_alloc(gidsetsize); |
242 | if (!group_info) | 249 | if (!group_info) |
243 | return -ENOMEM; | 250 | return -ENOMEM; |
244 | retval = groups_from_user(group_info, grouplist); | 251 | retval = groups_from_user(group_info, grouplist); |
245 | if (retval) { | 252 | if (retval) { |
246 | put_group_info(group_info); | 253 | put_group_info(group_info); |
247 | return retval; | 254 | return retval; |
248 | } | 255 | } |
249 | 256 | ||
250 | retval = set_current_groups(group_info); | 257 | retval = set_current_groups(group_info); |
251 | put_group_info(group_info); | 258 | put_group_info(group_info); |
252 | 259 | ||
253 | return retval; | 260 | return retval; |
254 | } | 261 | } |
255 | 262 | ||
256 | /* | 263 | /* |
257 | * Check whether we're fsgid/egid or in the supplemental group.. | 264 | * Check whether we're fsgid/egid or in the supplemental group.. |
258 | */ | 265 | */ |
259 | int in_group_p(kgid_t grp) | 266 | int in_group_p(kgid_t grp) |
260 | { | 267 | { |
261 | const struct cred *cred = current_cred(); | 268 | const struct cred *cred = current_cred(); |
262 | int retval = 1; | 269 | int retval = 1; |
263 | 270 | ||
264 | if (!gid_eq(grp, cred->fsgid)) | 271 | if (!gid_eq(grp, cred->fsgid)) |
265 | retval = groups_search(cred->group_info, grp); | 272 | retval = groups_search(cred->group_info, grp); |
266 | return retval; | 273 | return retval; |
267 | } | 274 | } |
268 | 275 | ||
269 | EXPORT_SYMBOL(in_group_p); | 276 | EXPORT_SYMBOL(in_group_p); |
270 | 277 | ||
271 | int in_egroup_p(kgid_t grp) | 278 | int in_egroup_p(kgid_t grp) |
272 | { | 279 | { |
273 | const struct cred *cred = current_cred(); | 280 | const struct cred *cred = current_cred(); |
274 | int retval = 1; | 281 | int retval = 1; |
275 | 282 | ||
276 | if (!gid_eq(grp, cred->egid)) | 283 | if (!gid_eq(grp, cred->egid)) |
277 | retval = groups_search(cred->group_info, grp); | 284 | retval = groups_search(cred->group_info, grp); |
278 | return retval; | 285 | return retval; |
279 | } | 286 | } |
280 | 287 | ||
281 | EXPORT_SYMBOL(in_egroup_p); | 288 | EXPORT_SYMBOL(in_egroup_p); |
282 | 289 |
kernel/uid16.c
1 | /* | 1 | /* |
2 | * Wrapper functions for 16bit uid back compatibility. All nicely tied | 2 | * Wrapper functions for 16bit uid back compatibility. All nicely tied |
3 | * together in the faint hope we can take the out in five years time. | 3 | * together in the faint hope we can take the out in five years time. |
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <linux/mm.h> | 6 | #include <linux/mm.h> |
7 | #include <linux/mman.h> | 7 | #include <linux/mman.h> |
8 | #include <linux/notifier.h> | 8 | #include <linux/notifier.h> |
9 | #include <linux/reboot.h> | 9 | #include <linux/reboot.h> |
10 | #include <linux/prctl.h> | 10 | #include <linux/prctl.h> |
11 | #include <linux/capability.h> | 11 | #include <linux/capability.h> |
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
13 | #include <linux/highuid.h> | 13 | #include <linux/highuid.h> |
14 | #include <linux/security.h> | 14 | #include <linux/security.h> |
15 | #include <linux/syscalls.h> | 15 | #include <linux/syscalls.h> |
16 | 16 | ||
17 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
18 | 18 | ||
19 | SYSCALL_DEFINE3(chown16, const char __user *, filename, old_uid_t, user, old_gid_t, group) | 19 | SYSCALL_DEFINE3(chown16, const char __user *, filename, old_uid_t, user, old_gid_t, group) |
20 | { | 20 | { |
21 | return sys_chown(filename, low2highuid(user), low2highgid(group)); | 21 | return sys_chown(filename, low2highuid(user), low2highgid(group)); |
22 | } | 22 | } |
23 | 23 | ||
24 | SYSCALL_DEFINE3(lchown16, const char __user *, filename, old_uid_t, user, old_gid_t, group) | 24 | SYSCALL_DEFINE3(lchown16, const char __user *, filename, old_uid_t, user, old_gid_t, group) |
25 | { | 25 | { |
26 | return sys_lchown(filename, low2highuid(user), low2highgid(group)); | 26 | return sys_lchown(filename, low2highuid(user), low2highgid(group)); |
27 | } | 27 | } |
28 | 28 | ||
29 | SYSCALL_DEFINE3(fchown16, unsigned int, fd, old_uid_t, user, old_gid_t, group) | 29 | SYSCALL_DEFINE3(fchown16, unsigned int, fd, old_uid_t, user, old_gid_t, group) |
30 | { | 30 | { |
31 | return sys_fchown(fd, low2highuid(user), low2highgid(group)); | 31 | return sys_fchown(fd, low2highuid(user), low2highgid(group)); |
32 | } | 32 | } |
33 | 33 | ||
34 | SYSCALL_DEFINE2(setregid16, old_gid_t, rgid, old_gid_t, egid) | 34 | SYSCALL_DEFINE2(setregid16, old_gid_t, rgid, old_gid_t, egid) |
35 | { | 35 | { |
36 | return sys_setregid(low2highgid(rgid), low2highgid(egid)); | 36 | return sys_setregid(low2highgid(rgid), low2highgid(egid)); |
37 | } | 37 | } |
38 | 38 | ||
39 | SYSCALL_DEFINE1(setgid16, old_gid_t, gid) | 39 | SYSCALL_DEFINE1(setgid16, old_gid_t, gid) |
40 | { | 40 | { |
41 | return sys_setgid(low2highgid(gid)); | 41 | return sys_setgid(low2highgid(gid)); |
42 | } | 42 | } |
43 | 43 | ||
44 | SYSCALL_DEFINE2(setreuid16, old_uid_t, ruid, old_uid_t, euid) | 44 | SYSCALL_DEFINE2(setreuid16, old_uid_t, ruid, old_uid_t, euid) |
45 | { | 45 | { |
46 | return sys_setreuid(low2highuid(ruid), low2highuid(euid)); | 46 | return sys_setreuid(low2highuid(ruid), low2highuid(euid)); |
47 | } | 47 | } |
48 | 48 | ||
49 | SYSCALL_DEFINE1(setuid16, old_uid_t, uid) | 49 | SYSCALL_DEFINE1(setuid16, old_uid_t, uid) |
50 | { | 50 | { |
51 | return sys_setuid(low2highuid(uid)); | 51 | return sys_setuid(low2highuid(uid)); |
52 | } | 52 | } |
53 | 53 | ||
54 | SYSCALL_DEFINE3(setresuid16, old_uid_t, ruid, old_uid_t, euid, old_uid_t, suid) | 54 | SYSCALL_DEFINE3(setresuid16, old_uid_t, ruid, old_uid_t, euid, old_uid_t, suid) |
55 | { | 55 | { |
56 | return sys_setresuid(low2highuid(ruid), low2highuid(euid), | 56 | return sys_setresuid(low2highuid(ruid), low2highuid(euid), |
57 | low2highuid(suid)); | 57 | low2highuid(suid)); |
58 | } | 58 | } |
59 | 59 | ||
60 | SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euidp, old_uid_t __user *, suidp) | 60 | SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euidp, old_uid_t __user *, suidp) |
61 | { | 61 | { |
62 | const struct cred *cred = current_cred(); | 62 | const struct cred *cred = current_cred(); |
63 | int retval; | 63 | int retval; |
64 | old_uid_t ruid, euid, suid; | 64 | old_uid_t ruid, euid, suid; |
65 | 65 | ||
66 | ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid)); | 66 | ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid)); |
67 | euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid)); | 67 | euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid)); |
68 | suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid)); | 68 | suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid)); |
69 | 69 | ||
70 | if (!(retval = put_user(ruid, ruidp)) && | 70 | if (!(retval = put_user(ruid, ruidp)) && |
71 | !(retval = put_user(euid, euidp))) | 71 | !(retval = put_user(euid, euidp))) |
72 | retval = put_user(suid, suidp); | 72 | retval = put_user(suid, suidp); |
73 | 73 | ||
74 | return retval; | 74 | return retval; |
75 | } | 75 | } |
76 | 76 | ||
77 | SYSCALL_DEFINE3(setresgid16, old_gid_t, rgid, old_gid_t, egid, old_gid_t, sgid) | 77 | SYSCALL_DEFINE3(setresgid16, old_gid_t, rgid, old_gid_t, egid, old_gid_t, sgid) |
78 | { | 78 | { |
79 | return sys_setresgid(low2highgid(rgid), low2highgid(egid), | 79 | return sys_setresgid(low2highgid(rgid), low2highgid(egid), |
80 | low2highgid(sgid)); | 80 | low2highgid(sgid)); |
81 | } | 81 | } |
82 | 82 | ||
83 | 83 | ||
84 | SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egidp, old_gid_t __user *, sgidp) | 84 | SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egidp, old_gid_t __user *, sgidp) |
85 | { | 85 | { |
86 | const struct cred *cred = current_cred(); | 86 | const struct cred *cred = current_cred(); |
87 | int retval; | 87 | int retval; |
88 | old_gid_t rgid, egid, sgid; | 88 | old_gid_t rgid, egid, sgid; |
89 | 89 | ||
90 | rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid)); | 90 | rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid)); |
91 | egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid)); | 91 | egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid)); |
92 | sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid)); | 92 | sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid)); |
93 | 93 | ||
94 | if (!(retval = put_user(rgid, rgidp)) && | 94 | if (!(retval = put_user(rgid, rgidp)) && |
95 | !(retval = put_user(egid, egidp))) | 95 | !(retval = put_user(egid, egidp))) |
96 | retval = put_user(sgid, sgidp); | 96 | retval = put_user(sgid, sgidp); |
97 | 97 | ||
98 | return retval; | 98 | return retval; |
99 | } | 99 | } |
100 | 100 | ||
101 | SYSCALL_DEFINE1(setfsuid16, old_uid_t, uid) | 101 | SYSCALL_DEFINE1(setfsuid16, old_uid_t, uid) |
102 | { | 102 | { |
103 | return sys_setfsuid(low2highuid(uid)); | 103 | return sys_setfsuid(low2highuid(uid)); |
104 | } | 104 | } |
105 | 105 | ||
106 | SYSCALL_DEFINE1(setfsgid16, old_gid_t, gid) | 106 | SYSCALL_DEFINE1(setfsgid16, old_gid_t, gid) |
107 | { | 107 | { |
108 | return sys_setfsgid(low2highgid(gid)); | 108 | return sys_setfsgid(low2highgid(gid)); |
109 | } | 109 | } |
110 | 110 | ||
111 | static int groups16_to_user(old_gid_t __user *grouplist, | 111 | static int groups16_to_user(old_gid_t __user *grouplist, |
112 | struct group_info *group_info) | 112 | struct group_info *group_info) |
113 | { | 113 | { |
114 | struct user_namespace *user_ns = current_user_ns(); | 114 | struct user_namespace *user_ns = current_user_ns(); |
115 | int i; | 115 | int i; |
116 | old_gid_t group; | 116 | old_gid_t group; |
117 | kgid_t kgid; | 117 | kgid_t kgid; |
118 | 118 | ||
119 | for (i = 0; i < group_info->ngroups; i++) { | 119 | for (i = 0; i < group_info->ngroups; i++) { |
120 | kgid = GROUP_AT(group_info, i); | 120 | kgid = GROUP_AT(group_info, i); |
121 | group = high2lowgid(from_kgid_munged(user_ns, kgid)); | 121 | group = high2lowgid(from_kgid_munged(user_ns, kgid)); |
122 | if (put_user(group, grouplist+i)) | 122 | if (put_user(group, grouplist+i)) |
123 | return -EFAULT; | 123 | return -EFAULT; |
124 | } | 124 | } |
125 | 125 | ||
126 | return 0; | 126 | return 0; |
127 | } | 127 | } |
128 | 128 | ||
129 | static int groups16_from_user(struct group_info *group_info, | 129 | static int groups16_from_user(struct group_info *group_info, |
130 | old_gid_t __user *grouplist) | 130 | old_gid_t __user *grouplist) |
131 | { | 131 | { |
132 | struct user_namespace *user_ns = current_user_ns(); | 132 | struct user_namespace *user_ns = current_user_ns(); |
133 | int i; | 133 | int i; |
134 | old_gid_t group; | 134 | old_gid_t group; |
135 | kgid_t kgid; | 135 | kgid_t kgid; |
136 | 136 | ||
137 | for (i = 0; i < group_info->ngroups; i++) { | 137 | for (i = 0; i < group_info->ngroups; i++) { |
138 | if (get_user(group, grouplist+i)) | 138 | if (get_user(group, grouplist+i)) |
139 | return -EFAULT; | 139 | return -EFAULT; |
140 | 140 | ||
141 | kgid = make_kgid(user_ns, low2highgid(group)); | 141 | kgid = make_kgid(user_ns, low2highgid(group)); |
142 | if (!gid_valid(kgid)) | 142 | if (!gid_valid(kgid)) |
143 | return -EINVAL; | 143 | return -EINVAL; |
144 | 144 | ||
145 | GROUP_AT(group_info, i) = kgid; | 145 | GROUP_AT(group_info, i) = kgid; |
146 | } | 146 | } |
147 | 147 | ||
148 | return 0; | 148 | return 0; |
149 | } | 149 | } |
150 | 150 | ||
151 | SYSCALL_DEFINE2(getgroups16, int, gidsetsize, old_gid_t __user *, grouplist) | 151 | SYSCALL_DEFINE2(getgroups16, int, gidsetsize, old_gid_t __user *, grouplist) |
152 | { | 152 | { |
153 | const struct cred *cred = current_cred(); | 153 | const struct cred *cred = current_cred(); |
154 | int i; | 154 | int i; |
155 | 155 | ||
156 | if (gidsetsize < 0) | 156 | if (gidsetsize < 0) |
157 | return -EINVAL; | 157 | return -EINVAL; |
158 | 158 | ||
159 | i = cred->group_info->ngroups; | 159 | i = cred->group_info->ngroups; |
160 | if (gidsetsize) { | 160 | if (gidsetsize) { |
161 | if (i > gidsetsize) { | 161 | if (i > gidsetsize) { |
162 | i = -EINVAL; | 162 | i = -EINVAL; |
163 | goto out; | 163 | goto out; |
164 | } | 164 | } |
165 | if (groups16_to_user(grouplist, cred->group_info)) { | 165 | if (groups16_to_user(grouplist, cred->group_info)) { |
166 | i = -EFAULT; | 166 | i = -EFAULT; |
167 | goto out; | 167 | goto out; |
168 | } | 168 | } |
169 | } | 169 | } |
170 | out: | 170 | out: |
171 | return i; | 171 | return i; |
172 | } | 172 | } |
173 | 173 | ||
174 | SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist) | 174 | SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist) |
175 | { | 175 | { |
176 | struct group_info *group_info; | 176 | struct group_info *group_info; |
177 | int retval; | 177 | int retval; |
178 | 178 | ||
179 | if (!ns_capable(current_user_ns(), CAP_SETGID)) | 179 | if (!may_setgroups()) |
180 | return -EPERM; | 180 | return -EPERM; |
181 | if ((unsigned)gidsetsize > NGROUPS_MAX) | 181 | if ((unsigned)gidsetsize > NGROUPS_MAX) |
182 | return -EINVAL; | 182 | return -EINVAL; |
183 | 183 | ||
184 | group_info = groups_alloc(gidsetsize); | 184 | group_info = groups_alloc(gidsetsize); |
185 | if (!group_info) | 185 | if (!group_info) |
186 | return -ENOMEM; | 186 | return -ENOMEM; |
187 | retval = groups16_from_user(group_info, grouplist); | 187 | retval = groups16_from_user(group_info, grouplist); |
188 | if (retval) { | 188 | if (retval) { |
189 | put_group_info(group_info); | 189 | put_group_info(group_info); |
190 | return retval; | 190 | return retval; |
191 | } | 191 | } |
192 | 192 | ||
193 | retval = set_current_groups(group_info); | 193 | retval = set_current_groups(group_info); |
194 | put_group_info(group_info); | 194 | put_group_info(group_info); |
195 | 195 | ||
196 | return retval; | 196 | return retval; |
197 | } | 197 | } |
198 | 198 | ||
199 | SYSCALL_DEFINE0(getuid16) | 199 | SYSCALL_DEFINE0(getuid16) |
200 | { | 200 | { |
201 | return high2lowuid(from_kuid_munged(current_user_ns(), current_uid())); | 201 | return high2lowuid(from_kuid_munged(current_user_ns(), current_uid())); |
202 | } | 202 | } |
203 | 203 | ||
204 | SYSCALL_DEFINE0(geteuid16) | 204 | SYSCALL_DEFINE0(geteuid16) |
205 | { | 205 | { |
206 | return high2lowuid(from_kuid_munged(current_user_ns(), current_euid())); | 206 | return high2lowuid(from_kuid_munged(current_user_ns(), current_euid())); |
207 | } | 207 | } |
208 | 208 | ||
209 | SYSCALL_DEFINE0(getgid16) | 209 | SYSCALL_DEFINE0(getgid16) |
210 | { | 210 | { |
211 | return high2lowgid(from_kgid_munged(current_user_ns(), current_gid())); | 211 | return high2lowgid(from_kgid_munged(current_user_ns(), current_gid())); |
212 | } | 212 | } |
213 | 213 | ||
214 | SYSCALL_DEFINE0(getegid16) | 214 | SYSCALL_DEFINE0(getegid16) |
215 | { | 215 | { |
216 | return high2lowgid(from_kgid_munged(current_user_ns(), current_egid())); | 216 | return high2lowgid(from_kgid_munged(current_user_ns(), current_egid())); |
217 | } | 217 | } |
218 | 218 |