Blame view
net/core/scm.c
8.3 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 5 |
/* scm.c - Socket level control messages processing. * * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * Alignment and value checking mods by Craig Metz |
1da177e4c Linux-2.6.12-rc2 |
6 7 8 9 |
*/ #include <linux/module.h> #include <linux/signal.h> |
4fc268d24 [PATCH] capable/c... |
10 |
#include <linux/capability.h> |
1da177e4c Linux-2.6.12-rc2 |
11 12 |
#include <linux/errno.h> #include <linux/sched.h> |
8703e8a46 sched/headers: Pr... |
13 |
#include <linux/sched/user.h> |
1da177e4c Linux-2.6.12-rc2 |
14 15 |
#include <linux/mm.h> #include <linux/kernel.h> |
1da177e4c Linux-2.6.12-rc2 |
16 17 18 19 20 21 22 23 |
#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 scm: Require CAP_... |
24 |
#include <linux/pid_namespace.h> |
b488893a3 pid namespaces: c... |
25 26 |
#include <linux/pid.h> #include <linux/nsproxy.h> |
5a0e3ad6a include cleanup: ... |
27 |
#include <linux/slab.h> |
9718475e6 socket: Add SO_TI... |
28 |
#include <linux/errqueue.h> |
1da177e4c Linux-2.6.12-rc2 |
29 |
|
7c0f6ba68 Replace <asm/uacc... |
30 |
#include <linux/uaccess.h> |
1da177e4c Linux-2.6.12-rc2 |
31 32 33 34 35 36 |
#include <net/protocol.h> #include <linux/skbuff.h> #include <net/sock.h> #include <net/compat.h> #include <net/scm.h> |
d84295067 net: net_cls: fd ... |
37 |
#include <net/cls_cgroup.h> |
1da177e4c Linux-2.6.12-rc2 |
38 39 40 |
/* |
4ec93edb1 [NET] CORE: Fix w... |
41 |
* Only allow a user to send credentials, that they could set with |
1da177e4c Linux-2.6.12-rc2 |
42 43 44 45 46 |
* setu(g)id. */ static __inline__ int scm_check_creds(struct ucred *creds) { |
86a264abe CRED: Wrap curren... |
47 |
const struct cred *cred = current_cred(); |
b2e4f544f userns: Convert n... |
48 49 50 51 52 |
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 CRED: Separate ta... |
53 |
|
92f28d973 scm: Require CAP_... |
54 |
if ((creds->pid == task_tgid_vnr(current) || |
d661684cf net: Check the co... |
55 |
ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) && |
b2e4f544f userns: Convert n... |
56 |
((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) || |
c7b96acf1 userns: Kill nso... |
57 |
uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) && |
b2e4f544f userns: Convert n... |
58 |
((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) || |
c7b96acf1 userns: Kill nso... |
59 |
gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) { |
1da177e4c Linux-2.6.12-rc2 |
60 61 62 63 64 65 66 67 68 69 70 |
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; |
1ff8cebf4 scm: remove use C... |
71 |
num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int); |
1da177e4c Linux-2.6.12-rc2 |
72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
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 scm: lower SCM_MA... |
86 |
fpl->max = SCM_MAX_FD; |
415e3d3e9 unix: correctly t... |
87 |
fpl->user = NULL; |
1da177e4c Linux-2.6.12-rc2 |
88 89 |
} fpp = &fpl->fp[fpl->count]; |
bba14de98 scm: lower SCM_MA... |
90 |
if (fpl->count + num > fpl->max) |
1da177e4c Linux-2.6.12-rc2 |
91 |
return -EINVAL; |
4ec93edb1 [NET] CORE: Fix w... |
92 |
|
1da177e4c Linux-2.6.12-rc2 |
93 94 95 |
/* * Verify the descriptors and increment the usage count. */ |
4ec93edb1 [NET] CORE: Fix w... |
96 |
|
1da177e4c Linux-2.6.12-rc2 |
97 98 99 100 |
for (i=0; i< num; i++) { int fd = fdp[i]; struct file *file; |
326be7b48 Allow passing O_P... |
101 |
if (fd < 0 || !(file = fget_raw(fd))) |
1da177e4c Linux-2.6.12-rc2 |
102 103 104 105 |
return -EBADF; *fpp++ = file; fpl->count++; } |
415e3d3e9 unix: correctly t... |
106 107 108 |
if (!fpl->user) fpl->user = get_uid(current_user()); |
1da177e4c Linux-2.6.12-rc2 |
109 110 111 112 113 114 115 116 117 118 |
return num; } void __scm_destroy(struct scm_cookie *scm) { struct scm_fp_list *fpl = scm->fp; int i; if (fpl) { scm->fp = NULL; |
6120d3dbb get rid of ->scm_... |
119 120 |
for (i=fpl->count-1; i>=0; i--) fput(fpl->fp[i]); |
415e3d3e9 unix: correctly t... |
121 |
free_uid(fpl->user); |
6120d3dbb get rid of ->scm_... |
122 |
kfree(fpl); |
1da177e4c Linux-2.6.12-rc2 |
123 124 |
} } |
9e34a5b51 net/core: EXPORT_... |
125 |
EXPORT_SYMBOL(__scm_destroy); |
1da177e4c Linux-2.6.12-rc2 |
126 127 128 129 130 |
int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) { struct cmsghdr *cmsg; int err; |
f95b414ed net: introduce he... |
131 |
for_each_cmsghdr(cmsg, msg) { |
1da177e4c Linux-2.6.12-rc2 |
132 133 134 135 136 137 |
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 [NET] CORE: Fix w... |
138 |
But if cmsg_level is not SOL_SOCKET, we do not check |
1da177e4c Linux-2.6.12-rc2 |
139 140 141 142 143 144 145 146 147 148 149 150 |
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 scm: Only support... |
151 152 |
if (!sock->ops || sock->ops->family != PF_UNIX) goto error; |
1da177e4c Linux-2.6.12-rc2 |
153 154 155 156 157 |
err=scm_fp_copy(cmsg, &p->fp); if (err<0) goto error; break; case SCM_CREDENTIALS: |
b2e4f544f userns: Convert n... |
158 |
{ |
dbe9a4173 scm: Don't use st... |
159 |
struct ucred creds; |
b2e4f544f userns: Convert n... |
160 161 |
kuid_t uid; kgid_t gid; |
1da177e4c Linux-2.6.12-rc2 |
162 163 |
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) goto error; |
dbe9a4173 scm: Don't use st... |
164 165 |
memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred)); err = scm_check_creds(&creds); |
1da177e4c Linux-2.6.12-rc2 |
166 167 |
if (err) goto error; |
257b5358b scm: Capture the ... |
168 |
|
dbe9a4173 scm: Don't use st... |
169 170 |
p->creds.pid = creds.pid; if (!p->pid || pid_vnr(p->pid) != creds.pid) { |
257b5358b scm: Capture the ... |
171 172 |
struct pid *pid; err = -ESRCH; |
dbe9a4173 scm: Don't use st... |
173 |
pid = find_get_pid(creds.pid); |
257b5358b scm: Capture the ... |
174 175 176 177 178 |
if (!pid) goto error; put_pid(p->pid); p->pid = pid; } |
b2e4f544f userns: Convert n... |
179 |
err = -EINVAL; |
dbe9a4173 scm: Don't use st... |
180 181 |
uid = make_kuid(current_user_ns(), creds.uid); gid = make_kgid(current_user_ns(), creds.gid); |
b2e4f544f userns: Convert n... |
182 183 |
if (!uid_valid(uid) || !gid_valid(gid)) goto error; |
dbe9a4173 scm: Don't use st... |
184 185 |
p->creds.uid = uid; p->creds.gid = gid; |
1da177e4c Linux-2.6.12-rc2 |
186 |
break; |
b2e4f544f userns: Convert n... |
187 |
} |
1da177e4c Linux-2.6.12-rc2 |
188 189 190 191 192 193 194 195 196 197 198 |
default: goto error; } } if (p->fp && !p->fp->count) { kfree(p->fp); p->fp = NULL; } return 0; |
4ec93edb1 [NET] CORE: Fix w... |
199 |
|
1da177e4c Linux-2.6.12-rc2 |
200 201 202 203 |
error: scm_destroy(p); return err; } |
9e34a5b51 net/core: EXPORT_... |
204 |
EXPORT_SYMBOL(__scm_send); |
1da177e4c Linux-2.6.12-rc2 |
205 206 207 |
int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) { |
1da177e4c Linux-2.6.12-rc2 |
208 |
int cmlen = CMSG_LEN(len); |
1da177e4c Linux-2.6.12-rc2 |
209 |
|
1f466e1f1 net: cleanly hand... |
210 |
if (msg->msg_flags & MSG_CMSG_COMPAT) |
1da177e4c Linux-2.6.12-rc2 |
211 |
return put_cmsg_compat(msg, level, type, len, data); |
1f466e1f1 net: cleanly hand... |
212 |
if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) { |
1da177e4c Linux-2.6.12-rc2 |
213 214 215 216 217 218 219 |
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; } |
1f466e1f1 net: cleanly hand... |
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
if (msg->msg_control_is_user) { struct cmsghdr __user *cm = msg->msg_control_user; struct cmsghdr cmhdr; cmhdr.cmsg_level = level; cmhdr.cmsg_type = type; cmhdr.cmsg_len = cmlen; if (copy_to_user(cm, &cmhdr, sizeof cmhdr) || copy_to_user(CMSG_USER_DATA(cm), data, cmlen - sizeof(*cm))) return -EFAULT; } else { struct cmsghdr *cm = msg->msg_control; cm->cmsg_level = level; cm->cmsg_type = type; cm->cmsg_len = cmlen; memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm)); } cmlen = min(CMSG_SPACE(len), msg->msg_controllen); |
1da177e4c Linux-2.6.12-rc2 |
241 242 |
msg->msg_control += cmlen; msg->msg_controllen -= cmlen; |
1f466e1f1 net: cleanly hand... |
243 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
244 |
} |
9e34a5b51 net/core: EXPORT_... |
245 |
EXPORT_SYMBOL(put_cmsg); |
1da177e4c Linux-2.6.12-rc2 |
246 |
|
9718475e6 socket: Add SO_TI... |
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
void put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) { struct scm_timestamping64 tss; int i; for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; } put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss); } EXPORT_SYMBOL(put_cmsg_scm_timestamping64); void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) { struct scm_timestamping tss; int i; |
0309f98f2 y2038: socket: re... |
265 266 267 268 |
for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; } |
9718475e6 socket: Add SO_TI... |
269 270 271 272 |
put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss); } EXPORT_SYMBOL(put_cmsg_scm_timestamping); |
2618d530d net/scm: cleanup ... |
273 274 275 276 277 278 |
static int scm_max_fds(struct msghdr *msg) { if (msg->msg_controllen <= sizeof(struct cmsghdr)) return 0; return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); } |
1da177e4c Linux-2.6.12-rc2 |
279 280 |
void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) { |
c0029de50 net/scm: Regulari... |
281 282 283 |
struct cmsghdr __user *cm = (__force struct cmsghdr __user *)msg->msg_control; unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; |
2618d530d net/scm: cleanup ... |
284 285 |
int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count); int __user *cmsg_data = CMSG_USER_DATA(cm); |
1da177e4c Linux-2.6.12-rc2 |
286 |
int err = 0, i; |
c0029de50 net/scm: Regulari... |
287 288 289 |
/* no use for FD passing from kernel space callers */ if (WARN_ON_ONCE(!msg->msg_control_is_user)) return; |
2618d530d net/scm: cleanup ... |
290 |
if (msg->msg_flags & MSG_CMSG_COMPAT) { |
1da177e4c Linux-2.6.12-rc2 |
291 292 293 |
scm_detach_fds_compat(msg, scm); return; } |
2618d530d net/scm: cleanup ... |
294 |
for (i = 0; i < fdmax; i++) { |
665906104 fs: Move __scm_in... |
295 |
err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags); |
deefa7f35 fs: Add receive_f... |
296 |
if (err < 0) |
1da177e4c Linux-2.6.12-rc2 |
297 |
break; |
1da177e4c Linux-2.6.12-rc2 |
298 |
} |
c0029de50 net/scm: Regulari... |
299 |
if (i > 0) { |
2618d530d net/scm: cleanup ... |
300 |
int cmlen = CMSG_LEN(i * sizeof(int)); |
effee6a00 [NET]: File descr... |
301 |
err = put_user(SOL_SOCKET, &cm->cmsg_level); |
1da177e4c Linux-2.6.12-rc2 |
302 303 304 305 306 |
if (!err) err = put_user(SCM_RIGHTS, &cm->cmsg_type); if (!err) err = put_user(cmlen, &cm->cmsg_len); if (!err) { |
2618d530d net/scm: cleanup ... |
307 |
cmlen = CMSG_SPACE(i * sizeof(int)); |
6900317f5 net, scm: fix PaX... |
308 309 |
if (msg->msg_controllen < cmlen) cmlen = msg->msg_controllen; |
1da177e4c Linux-2.6.12-rc2 |
310 311 312 313 |
msg->msg_control += cmlen; msg->msg_controllen -= cmlen; } } |
2618d530d net/scm: cleanup ... |
314 315 |
if (i < scm->fp->count || (scm->fp->count && fdmax <= 0)) |
1da177e4c Linux-2.6.12-rc2 |
316 317 318 |
msg->msg_flags |= MSG_CTRUNC; /* |
2618d530d net/scm: cleanup ... |
319 320 |
* All of the files that fit in the message have had their usage counts * incremented, so we just free the list. |
1da177e4c Linux-2.6.12-rc2 |
321 322 323 |
*/ __scm_destroy(scm); } |
9e34a5b51 net/core: EXPORT_... |
324 |
EXPORT_SYMBOL(scm_detach_fds); |
1da177e4c Linux-2.6.12-rc2 |
325 326 327 328 329 330 331 332 |
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 scm: lower SCM_MA... |
333 334 |
new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
335 |
if (new_fpl) { |
bba14de98 scm: lower SCM_MA... |
336 |
for (i = 0; i < fpl->count; i++) |
1da177e4c Linux-2.6.12-rc2 |
337 |
get_file(fpl->fp[i]); |
bba14de98 scm: lower SCM_MA... |
338 |
new_fpl->max = new_fpl->count; |
415e3d3e9 unix: correctly t... |
339 |
new_fpl->user = get_uid(fpl->user); |
1da177e4c Linux-2.6.12-rc2 |
340 341 342 |
} return new_fpl; } |
1da177e4c Linux-2.6.12-rc2 |
343 |
EXPORT_SYMBOL(scm_fp_dup); |