Merge tag 'upstream-3.7-rc1' of git://git.infradead.org/linux-ubifs
[linux-3.10.git] / kernel / uid16.c
1 /*
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.
4  */
5
6 #include <linux/mm.h>
7 #include <linux/mman.h>
8 #include <linux/notifier.h>
9 #include <linux/reboot.h>
10 #include <linux/prctl.h>
11 #include <linux/capability.h>
12 #include <linux/init.h>
13 #include <linux/highuid.h>
14 #include <linux/security.h>
15 #include <linux/syscalls.h>
16
17 #include <asm/uaccess.h>
18
19 SYSCALL_DEFINE3(chown16, const char __user *, filename, old_uid_t, user, old_gid_t, group)
20 {
21         long ret = sys_chown(filename, low2highuid(user), low2highgid(group));
22         /* avoid REGPARM breakage on x86: */
23         asmlinkage_protect(3, ret, filename, user, group);
24         return ret;
25 }
26
27 SYSCALL_DEFINE3(lchown16, const char __user *, filename, old_uid_t, user, old_gid_t, group)
28 {
29         long ret = sys_lchown(filename, low2highuid(user), low2highgid(group));
30         /* avoid REGPARM breakage on x86: */
31         asmlinkage_protect(3, ret, filename, user, group);
32         return ret;
33 }
34
35 SYSCALL_DEFINE3(fchown16, unsigned int, fd, old_uid_t, user, old_gid_t, group)
36 {
37         long ret = sys_fchown(fd, low2highuid(user), low2highgid(group));
38         /* avoid REGPARM breakage on x86: */
39         asmlinkage_protect(3, ret, fd, user, group);
40         return ret;
41 }
42
43 SYSCALL_DEFINE2(setregid16, old_gid_t, rgid, old_gid_t, egid)
44 {
45         long ret = sys_setregid(low2highgid(rgid), low2highgid(egid));
46         /* avoid REGPARM breakage on x86: */
47         asmlinkage_protect(2, ret, rgid, egid);
48         return ret;
49 }
50
51 SYSCALL_DEFINE1(setgid16, old_gid_t, gid)
52 {
53         long ret = sys_setgid(low2highgid(gid));
54         /* avoid REGPARM breakage on x86: */
55         asmlinkage_protect(1, ret, gid);
56         return ret;
57 }
58
59 SYSCALL_DEFINE2(setreuid16, old_uid_t, ruid, old_uid_t, euid)
60 {
61         long ret = sys_setreuid(low2highuid(ruid), low2highuid(euid));
62         /* avoid REGPARM breakage on x86: */
63         asmlinkage_protect(2, ret, ruid, euid);
64         return ret;
65 }
66
67 SYSCALL_DEFINE1(setuid16, old_uid_t, uid)
68 {
69         long ret = sys_setuid(low2highuid(uid));
70         /* avoid REGPARM breakage on x86: */
71         asmlinkage_protect(1, ret, uid);
72         return ret;
73 }
74
75 SYSCALL_DEFINE3(setresuid16, old_uid_t, ruid, old_uid_t, euid, old_uid_t, suid)
76 {
77         long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid),
78                                  low2highuid(suid));
79         /* avoid REGPARM breakage on x86: */
80         asmlinkage_protect(3, ret, ruid, euid, suid);
81         return ret;
82 }
83
84 SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euidp, old_uid_t __user *, suidp)
85 {
86         const struct cred *cred = current_cred();
87         int retval;
88         old_uid_t ruid, euid, suid;
89
90         ruid = high2lowuid(from_kuid_munged(cred->user_ns, cred->uid));
91         euid = high2lowuid(from_kuid_munged(cred->user_ns, cred->euid));
92         suid = high2lowuid(from_kuid_munged(cred->user_ns, cred->suid));
93
94         if (!(retval   = put_user(ruid, ruidp)) &&
95             !(retval   = put_user(euid, euidp)))
96                 retval = put_user(suid, suidp);
97
98         return retval;
99 }
100
101 SYSCALL_DEFINE3(setresgid16, old_gid_t, rgid, old_gid_t, egid, old_gid_t, sgid)
102 {
103         long ret = sys_setresgid(low2highgid(rgid), low2highgid(egid),
104                                  low2highgid(sgid));
105         /* avoid REGPARM breakage on x86: */
106         asmlinkage_protect(3, ret, rgid, egid, sgid);
107         return ret;
108 }
109
110
111 SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egidp, old_gid_t __user *, sgidp)
112 {
113         const struct cred *cred = current_cred();
114         int retval;
115         old_gid_t rgid, egid, sgid;
116
117         rgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->gid));
118         egid = high2lowgid(from_kgid_munged(cred->user_ns, cred->egid));
119         sgid = high2lowgid(from_kgid_munged(cred->user_ns, cred->sgid));
120
121         if (!(retval   = put_user(rgid, rgidp)) &&
122             !(retval   = put_user(egid, egidp)))
123                 retval = put_user(sgid, sgidp);
124
125         return retval;
126 }
127
128 SYSCALL_DEFINE1(setfsuid16, old_uid_t, uid)
129 {
130         long ret = sys_setfsuid(low2highuid(uid));
131         /* avoid REGPARM breakage on x86: */
132         asmlinkage_protect(1, ret, uid);
133         return ret;
134 }
135
136 SYSCALL_DEFINE1(setfsgid16, old_gid_t, gid)
137 {
138         long ret = sys_setfsgid(low2highgid(gid));
139         /* avoid REGPARM breakage on x86: */
140         asmlinkage_protect(1, ret, gid);
141         return ret;
142 }
143
144 static int groups16_to_user(old_gid_t __user *grouplist,
145     struct group_info *group_info)
146 {
147         struct user_namespace *user_ns = current_user_ns();
148         int i;
149         old_gid_t group;
150         kgid_t kgid;
151
152         for (i = 0; i < group_info->ngroups; i++) {
153                 kgid = GROUP_AT(group_info, i);
154                 group = high2lowgid(from_kgid_munged(user_ns, kgid));
155                 if (put_user(group, grouplist+i))
156                         return -EFAULT;
157         }
158
159         return 0;
160 }
161
162 static int groups16_from_user(struct group_info *group_info,
163     old_gid_t __user *grouplist)
164 {
165         struct user_namespace *user_ns = current_user_ns();
166         int i;
167         old_gid_t group;
168         kgid_t kgid;
169
170         for (i = 0; i < group_info->ngroups; i++) {
171                 if (get_user(group, grouplist+i))
172                         return  -EFAULT;
173
174                 kgid = make_kgid(user_ns, low2highgid(group));
175                 if (!gid_valid(kgid))
176                         return -EINVAL;
177
178                 GROUP_AT(group_info, i) = kgid;
179         }
180
181         return 0;
182 }
183
184 SYSCALL_DEFINE2(getgroups16, int, gidsetsize, old_gid_t __user *, grouplist)
185 {
186         const struct cred *cred = current_cred();
187         int i;
188
189         if (gidsetsize < 0)
190                 return -EINVAL;
191
192         i = cred->group_info->ngroups;
193         if (gidsetsize) {
194                 if (i > gidsetsize) {
195                         i = -EINVAL;
196                         goto out;
197                 }
198                 if (groups16_to_user(grouplist, cred->group_info)) {
199                         i = -EFAULT;
200                         goto out;
201                 }
202         }
203 out:
204         return i;
205 }
206
207 SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist)
208 {
209         struct group_info *group_info;
210         int retval;
211
212         if (!nsown_capable(CAP_SETGID))
213                 return -EPERM;
214         if ((unsigned)gidsetsize > NGROUPS_MAX)
215                 return -EINVAL;
216
217         group_info = groups_alloc(gidsetsize);
218         if (!group_info)
219                 return -ENOMEM;
220         retval = groups16_from_user(group_info, grouplist);
221         if (retval) {
222                 put_group_info(group_info);
223                 return retval;
224         }
225
226         retval = set_current_groups(group_info);
227         put_group_info(group_info);
228
229         return retval;
230 }
231
232 SYSCALL_DEFINE0(getuid16)
233 {
234         return high2lowuid(from_kuid_munged(current_user_ns(), current_uid()));
235 }
236
237 SYSCALL_DEFINE0(geteuid16)
238 {
239         return high2lowuid(from_kuid_munged(current_user_ns(), current_euid()));
240 }
241
242 SYSCALL_DEFINE0(getgid16)
243 {
244         return high2lowgid(from_kgid_munged(current_user_ns(), current_gid()));
245 }
246
247 SYSCALL_DEFINE0(getegid16)
248 {
249         return high2lowgid(from_kgid_munged(current_user_ns(), current_egid()));
250 }