Blame view
net/core/scm.c
7.61 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* scm.c - Socket level control messages processing. * * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * Alignment and value checking mods by Craig Metz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include <linux/module.h> #include <linux/signal.h> |
4fc268d24
|
14 |
#include <linux/capability.h> |
1da177e4c
|
15 16 17 18 |
#include <linux/errno.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/kernel.h> |
1da177e4c
|
19 20 21 22 23 24 25 26 |
#include <linux/stat.h> #include <linux/socket.h> #include <linux/file.h> #include <linux/fcntl.h> #include <linux/net.h> #include <linux/interrupt.h> #include <linux/netdevice.h> #include <linux/security.h> |
92f28d973
|
27 |
#include <linux/pid_namespace.h> |
b488893a3
|
28 29 |
#include <linux/pid.h> #include <linux/nsproxy.h> |
5a0e3ad6a
|
30 |
#include <linux/slab.h> |
1da177e4c
|
31 |
|
1da177e4c
|
32 33 34 35 36 37 38 |
#include <asm/uaccess.h> #include <net/protocol.h> #include <linux/skbuff.h> #include <net/sock.h> #include <net/compat.h> #include <net/scm.h> |
d84295067
|
39 |
#include <net/cls_cgroup.h> |
1da177e4c
|
40 41 42 |
/* |
4ec93edb1
|
43 |
* Only allow a user to send credentials, that they could set with |
1da177e4c
|
44 45 46 47 48 |
* setu(g)id. */ static __inline__ int scm_check_creds(struct ucred *creds) { |
86a264abe
|
49 |
const struct cred *cred = current_cred(); |
b2e4f544f
|
50 51 52 53 54 |
kuid_t uid = make_kuid(cred->user_ns, creds->uid); kgid_t gid = make_kgid(cred->user_ns, creds->gid); if (!uid_valid(uid) || !gid_valid(gid)) return -EINVAL; |
b6dff3ec5
|
55 |
|
92f28d973
|
56 |
if ((creds->pid == task_tgid_vnr(current) || |
d661684cf
|
57 |
ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) && |
b2e4f544f
|
58 |
((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) || |
c7b96acf1
|
59 |
uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) && |
b2e4f544f
|
60 |
((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) || |
c7b96acf1
|
61 |
gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) { |
1da177e4c
|
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
return 0; } return -EPERM; } static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) { int *fdp = (int*)CMSG_DATA(cmsg); struct scm_fp_list *fpl = *fplp; struct file **fpp; int i, num; num = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))/sizeof(int); if (num <= 0) return 0; if (num > SCM_MAX_FD) return -EINVAL; if (!fpl) { fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL); if (!fpl) return -ENOMEM; *fplp = fpl; fpl->count = 0; |
bba14de98
|
89 |
fpl->max = SCM_MAX_FD; |
1da177e4c
|
90 91 |
} fpp = &fpl->fp[fpl->count]; |
bba14de98
|
92 |
if (fpl->count + num > fpl->max) |
1da177e4c
|
93 |
return -EINVAL; |
4ec93edb1
|
94 |
|
1da177e4c
|
95 96 97 |
/* * Verify the descriptors and increment the usage count. */ |
4ec93edb1
|
98 |
|
1da177e4c
|
99 100 101 102 |
for (i=0; i< num; i++) { int fd = fdp[i]; struct file *file; |
326be7b48
|
103 |
if (fd < 0 || !(file = fget_raw(fd))) |
1da177e4c
|
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
return -EBADF; *fpp++ = file; fpl->count++; } return num; } void __scm_destroy(struct scm_cookie *scm) { struct scm_fp_list *fpl = scm->fp; int i; if (fpl) { scm->fp = NULL; |
6120d3dbb
|
118 119 120 |
for (i=fpl->count-1; i>=0; i--) fput(fpl->fp[i]); kfree(fpl); |
1da177e4c
|
121 122 |
} } |
9e34a5b51
|
123 |
EXPORT_SYMBOL(__scm_destroy); |
1da177e4c
|
124 125 126 127 128 |
int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) { struct cmsghdr *cmsg; int err; |
f95b414ed
|
129 |
for_each_cmsghdr(cmsg, msg) { |
1da177e4c
|
130 131 132 133 134 135 |
err = -EINVAL; /* Verify that cmsg_len is at least sizeof(struct cmsghdr) */ /* The first check was omitted in <= 2.2.5. The reasoning was that parser checks cmsg_len in any case, so that additional check would be work duplication. |
4ec93edb1
|
136 |
But if cmsg_level is not SOL_SOCKET, we do not check |
1da177e4c
|
137 138 139 140 141 142 143 144 145 146 147 148 |
for too short ancillary data object at all! Oops. OK, let's add it... */ if (!CMSG_OK(msg, cmsg)) goto error; if (cmsg->cmsg_level != SOL_SOCKET) continue; switch (cmsg->cmsg_type) { case SCM_RIGHTS: |
76dadd76c
|
149 150 |
if (!sock->ops || sock->ops->family != PF_UNIX) goto error; |
1da177e4c
|
151 152 153 154 155 |
err=scm_fp_copy(cmsg, &p->fp); if (err<0) goto error; break; case SCM_CREDENTIALS: |
b2e4f544f
|
156 |
{ |
dbe9a4173
|
157 |
struct ucred creds; |
b2e4f544f
|
158 159 |
kuid_t uid; kgid_t gid; |
1da177e4c
|
160 161 |
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) goto error; |
dbe9a4173
|
162 163 |
memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred)); err = scm_check_creds(&creds); |
1da177e4c
|
164 165 |
if (err) goto error; |
257b5358b
|
166 |
|
dbe9a4173
|
167 168 |
p->creds.pid = creds.pid; if (!p->pid || pid_vnr(p->pid) != creds.pid) { |
257b5358b
|
169 170 |
struct pid *pid; err = -ESRCH; |
dbe9a4173
|
171 |
pid = find_get_pid(creds.pid); |
257b5358b
|
172 173 174 175 176 |
if (!pid) goto error; put_pid(p->pid); p->pid = pid; } |
b2e4f544f
|
177 |
err = -EINVAL; |
dbe9a4173
|
178 179 |
uid = make_kuid(current_user_ns(), creds.uid); gid = make_kgid(current_user_ns(), creds.gid); |
b2e4f544f
|
180 181 |
if (!uid_valid(uid) || !gid_valid(gid)) goto error; |
dbe9a4173
|
182 183 |
p->creds.uid = uid; p->creds.gid = gid; |
1da177e4c
|
184 |
break; |
b2e4f544f
|
185 |
} |
1da177e4c
|
186 187 188 189 190 191 192 193 194 195 196 |
default: goto error; } } if (p->fp && !p->fp->count) { kfree(p->fp); p->fp = NULL; } return 0; |
4ec93edb1
|
197 |
|
1da177e4c
|
198 199 200 201 |
error: scm_destroy(p); return err; } |
9e34a5b51
|
202 |
EXPORT_SYMBOL(__scm_send); |
1da177e4c
|
203 204 205 |
int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) { |
cfcabdcc2
|
206 207 |
struct cmsghdr __user *cm = (__force struct cmsghdr __user *)msg->msg_control; |
1da177e4c
|
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
struct cmsghdr cmhdr; int cmlen = CMSG_LEN(len); int err; if (MSG_CMSG_COMPAT & msg->msg_flags) return put_cmsg_compat(msg, level, type, len, data); if (cm==NULL || msg->msg_controllen < sizeof(*cm)) { msg->msg_flags |= MSG_CTRUNC; return 0; /* XXX: return error? check spec. */ } if (msg->msg_controllen < cmlen) { msg->msg_flags |= MSG_CTRUNC; cmlen = msg->msg_controllen; } cmhdr.cmsg_level = level; cmhdr.cmsg_type = type; cmhdr.cmsg_len = cmlen; err = -EFAULT; if (copy_to_user(cm, &cmhdr, sizeof cmhdr)) |
4ec93edb1
|
229 |
goto out; |
1da177e4c
|
230 231 232 |
if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr))) goto out; cmlen = CMSG_SPACE(len); |
1ac70e7ad
|
233 234 |
if (msg->msg_controllen < cmlen) cmlen = msg->msg_controllen; |
1da177e4c
|
235 236 237 238 239 240 |
msg->msg_control += cmlen; msg->msg_controllen -= cmlen; err = 0; out: return err; } |
9e34a5b51
|
241 |
EXPORT_SYMBOL(put_cmsg); |
1da177e4c
|
242 243 244 |
void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) { |
cfcabdcc2
|
245 246 |
struct cmsghdr __user *cm = (__force struct cmsghdr __user*)msg->msg_control; |
1da177e4c
|
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
int fdmax = 0; int fdnum = scm->fp->count; struct file **fp = scm->fp->fp; int __user *cmfptr; int err = 0, i; if (MSG_CMSG_COMPAT & msg->msg_flags) { scm_detach_fds_compat(msg, scm); return; } if (msg->msg_controllen > sizeof(struct cmsghdr)) fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int)); if (fdnum < fdmax) fdmax = fdnum; |
cfcabdcc2
|
265 266 |
for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax; i++, cmfptr++) |
1da177e4c
|
267 |
{ |
48a87cc26
|
268 |
struct socket *sock; |
1da177e4c
|
269 270 271 272 |
int new_fd; err = security_file_receive(fp[i]); if (err) break; |
4a19542e5
|
273 274 |
err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & msg->msg_flags ? O_CLOEXEC : 0); |
1da177e4c
|
275 276 277 278 279 280 281 282 283 |
if (err < 0) break; new_fd = err; err = put_user(new_fd, cmfptr); if (err) { put_unused_fd(new_fd); break; } /* Bump the usage count and install the file. */ |
48a87cc26
|
284 |
sock = sock_from_file(fp[i], &err); |
d84295067
|
285 |
if (sock) { |
6ffd46410
|
286 |
sock_update_netprioidx(sock->sk); |
211d2f97e
|
287 |
sock_update_classid(sock->sk); |
d84295067
|
288 |
} |
cb0942b81
|
289 |
fd_install(new_fd, get_file(fp[i])); |
1da177e4c
|
290 291 292 293 294 |
} if (i > 0) { int cmlen = CMSG_LEN(i*sizeof(int)); |
effee6a00
|
295 |
err = put_user(SOL_SOCKET, &cm->cmsg_level); |
1da177e4c
|
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
if (!err) err = put_user(SCM_RIGHTS, &cm->cmsg_type); if (!err) err = put_user(cmlen, &cm->cmsg_len); if (!err) { cmlen = CMSG_SPACE(i*sizeof(int)); msg->msg_control += cmlen; msg->msg_controllen -= cmlen; } } if (i < fdnum || (fdnum && fdmax <= 0)) msg->msg_flags |= MSG_CTRUNC; /* * All of the files that fit in the message have had their * usage counts incremented, so we just free the list. */ __scm_destroy(scm); } |
9e34a5b51
|
315 |
EXPORT_SYMBOL(scm_detach_fds); |
1da177e4c
|
316 317 318 319 320 321 322 323 |
struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl) { struct scm_fp_list *new_fpl; int i; if (!fpl) return NULL; |
bba14de98
|
324 325 |
new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]), GFP_KERNEL); |
1da177e4c
|
326 |
if (new_fpl) { |
bba14de98
|
327 |
for (i = 0; i < fpl->count; i++) |
1da177e4c
|
328 |
get_file(fpl->fp[i]); |
bba14de98
|
329 |
new_fpl->max = new_fpl->count; |
1da177e4c
|
330 331 332 |
} return new_fpl; } |
1da177e4c
|
333 |
EXPORT_SYMBOL(scm_fp_dup); |