.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* scm.c - Socket level control messages processing. |
---|
2 | 3 | * |
---|
3 | 4 | * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
---|
4 | 5 | * Alignment and value checking mods by Craig Metz |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or |
---|
7 | | - * modify it under the terms of the GNU General Public License |
---|
8 | | - * as published by the Free Software Foundation; either version |
---|
9 | | - * 2 of the License, or (at your option) any later version. |
---|
10 | 6 | */ |
---|
11 | 7 | |
---|
12 | 8 | #include <linux/module.h> |
---|
.. | .. |
---|
29 | 25 | #include <linux/pid.h> |
---|
30 | 26 | #include <linux/nsproxy.h> |
---|
31 | 27 | #include <linux/slab.h> |
---|
| 28 | +#include <linux/errqueue.h> |
---|
32 | 29 | |
---|
33 | 30 | #include <linux/uaccess.h> |
---|
34 | 31 | |
---|
.. | .. |
---|
215 | 212 | |
---|
216 | 213 | int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) |
---|
217 | 214 | { |
---|
218 | | - struct cmsghdr __user *cm |
---|
219 | | - = (__force struct cmsghdr __user *)msg->msg_control; |
---|
220 | | - struct cmsghdr cmhdr; |
---|
221 | 215 | int cmlen = CMSG_LEN(len); |
---|
222 | | - int err; |
---|
223 | 216 | |
---|
224 | | - if (MSG_CMSG_COMPAT & msg->msg_flags) |
---|
| 217 | + if (msg->msg_flags & MSG_CMSG_COMPAT) |
---|
225 | 218 | return put_cmsg_compat(msg, level, type, len, data); |
---|
226 | 219 | |
---|
227 | | - if (cm==NULL || msg->msg_controllen < sizeof(*cm)) { |
---|
| 220 | + if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) { |
---|
228 | 221 | msg->msg_flags |= MSG_CTRUNC; |
---|
229 | 222 | return 0; /* XXX: return error? check spec. */ |
---|
230 | 223 | } |
---|
.. | .. |
---|
232 | 225 | msg->msg_flags |= MSG_CTRUNC; |
---|
233 | 226 | cmlen = msg->msg_controllen; |
---|
234 | 227 | } |
---|
235 | | - cmhdr.cmsg_level = level; |
---|
236 | | - cmhdr.cmsg_type = type; |
---|
237 | | - cmhdr.cmsg_len = cmlen; |
---|
238 | 228 | |
---|
239 | | - err = -EFAULT; |
---|
240 | | - if (copy_to_user(cm, &cmhdr, sizeof cmhdr)) |
---|
241 | | - goto out; |
---|
242 | | - if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr))) |
---|
243 | | - goto out; |
---|
244 | | - cmlen = CMSG_SPACE(len); |
---|
245 | | - if (msg->msg_controllen < cmlen) |
---|
246 | | - cmlen = msg->msg_controllen; |
---|
| 229 | + if (msg->msg_control_is_user) { |
---|
| 230 | + struct cmsghdr __user *cm = msg->msg_control_user; |
---|
| 231 | + struct cmsghdr cmhdr; |
---|
| 232 | + |
---|
| 233 | + cmhdr.cmsg_level = level; |
---|
| 234 | + cmhdr.cmsg_type = type; |
---|
| 235 | + cmhdr.cmsg_len = cmlen; |
---|
| 236 | + if (copy_to_user(cm, &cmhdr, sizeof cmhdr) || |
---|
| 237 | + copy_to_user(CMSG_USER_DATA(cm), data, cmlen - sizeof(*cm))) |
---|
| 238 | + return -EFAULT; |
---|
| 239 | + } else { |
---|
| 240 | + struct cmsghdr *cm = msg->msg_control; |
---|
| 241 | + |
---|
| 242 | + cm->cmsg_level = level; |
---|
| 243 | + cm->cmsg_type = type; |
---|
| 244 | + cm->cmsg_len = cmlen; |
---|
| 245 | + memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm)); |
---|
| 246 | + } |
---|
| 247 | + |
---|
| 248 | + cmlen = min(CMSG_SPACE(len), msg->msg_controllen); |
---|
247 | 249 | msg->msg_control += cmlen; |
---|
248 | 250 | msg->msg_controllen -= cmlen; |
---|
249 | | - err = 0; |
---|
250 | | -out: |
---|
251 | | - return err; |
---|
| 251 | + return 0; |
---|
252 | 252 | } |
---|
253 | 253 | EXPORT_SYMBOL(put_cmsg); |
---|
254 | 254 | |
---|
| 255 | +void put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) |
---|
| 256 | +{ |
---|
| 257 | + struct scm_timestamping64 tss; |
---|
| 258 | + int i; |
---|
| 259 | + |
---|
| 260 | + for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { |
---|
| 261 | + tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; |
---|
| 262 | + tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; |
---|
| 263 | + } |
---|
| 264 | + |
---|
| 265 | + put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss); |
---|
| 266 | +} |
---|
| 267 | +EXPORT_SYMBOL(put_cmsg_scm_timestamping64); |
---|
| 268 | + |
---|
| 269 | +void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) |
---|
| 270 | +{ |
---|
| 271 | + struct scm_timestamping tss; |
---|
| 272 | + int i; |
---|
| 273 | + |
---|
| 274 | + for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { |
---|
| 275 | + tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; |
---|
| 276 | + tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; |
---|
| 277 | + } |
---|
| 278 | + |
---|
| 279 | + put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss); |
---|
| 280 | +} |
---|
| 281 | +EXPORT_SYMBOL(put_cmsg_scm_timestamping); |
---|
| 282 | + |
---|
| 283 | +static int scm_max_fds(struct msghdr *msg) |
---|
| 284 | +{ |
---|
| 285 | + if (msg->msg_controllen <= sizeof(struct cmsghdr)) |
---|
| 286 | + return 0; |
---|
| 287 | + return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); |
---|
| 288 | +} |
---|
| 289 | + |
---|
255 | 290 | void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) |
---|
256 | 291 | { |
---|
257 | | - struct cmsghdr __user *cm |
---|
258 | | - = (__force struct cmsghdr __user*)msg->msg_control; |
---|
259 | | - |
---|
260 | | - int fdmax = 0; |
---|
261 | | - int fdnum = scm->fp->count; |
---|
262 | | - struct file **fp = scm->fp->fp; |
---|
263 | | - int __user *cmfptr; |
---|
| 292 | + struct cmsghdr __user *cm = |
---|
| 293 | + (__force struct cmsghdr __user *)msg->msg_control; |
---|
| 294 | + unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; |
---|
| 295 | + int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count); |
---|
| 296 | + int __user *cmsg_data = CMSG_USER_DATA(cm); |
---|
264 | 297 | int err = 0, i; |
---|
265 | 298 | |
---|
266 | | - if (MSG_CMSG_COMPAT & msg->msg_flags) { |
---|
| 299 | + /* no use for FD passing from kernel space callers */ |
---|
| 300 | + if (WARN_ON_ONCE(!msg->msg_control_is_user)) |
---|
| 301 | + return; |
---|
| 302 | + |
---|
| 303 | + if (msg->msg_flags & MSG_CMSG_COMPAT) { |
---|
267 | 304 | scm_detach_fds_compat(msg, scm); |
---|
268 | 305 | return; |
---|
269 | 306 | } |
---|
270 | 307 | |
---|
271 | | - if (msg->msg_controllen > sizeof(struct cmsghdr)) |
---|
272 | | - fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr)) |
---|
273 | | - / sizeof(int)); |
---|
274 | | - |
---|
275 | | - if (fdnum < fdmax) |
---|
276 | | - fdmax = fdnum; |
---|
277 | | - |
---|
278 | | - for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax; |
---|
279 | | - i++, cmfptr++) |
---|
280 | | - { |
---|
281 | | - struct socket *sock; |
---|
282 | | - int new_fd; |
---|
283 | | - err = security_file_receive(fp[i]); |
---|
284 | | - if (err) |
---|
285 | | - break; |
---|
286 | | - err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & msg->msg_flags |
---|
287 | | - ? O_CLOEXEC : 0); |
---|
| 308 | + for (i = 0; i < fdmax; i++) { |
---|
| 309 | + err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags); |
---|
288 | 310 | if (err < 0) |
---|
289 | 311 | break; |
---|
290 | | - new_fd = err; |
---|
291 | | - err = put_user(new_fd, cmfptr); |
---|
292 | | - if (err) { |
---|
293 | | - put_unused_fd(new_fd); |
---|
294 | | - break; |
---|
295 | | - } |
---|
296 | | - /* Bump the usage count and install the file. */ |
---|
297 | | - sock = sock_from_file(fp[i], &err); |
---|
298 | | - if (sock) { |
---|
299 | | - sock_update_netprioidx(&sock->sk->sk_cgrp_data); |
---|
300 | | - sock_update_classid(&sock->sk->sk_cgrp_data); |
---|
301 | | - } |
---|
302 | | - fd_install(new_fd, get_file(fp[i])); |
---|
303 | 312 | } |
---|
304 | 313 | |
---|
305 | | - if (i > 0) |
---|
306 | | - { |
---|
307 | | - int cmlen = CMSG_LEN(i*sizeof(int)); |
---|
| 314 | + if (i > 0) { |
---|
| 315 | + int cmlen = CMSG_LEN(i * sizeof(int)); |
---|
| 316 | + |
---|
308 | 317 | err = put_user(SOL_SOCKET, &cm->cmsg_level); |
---|
309 | 318 | if (!err) |
---|
310 | 319 | err = put_user(SCM_RIGHTS, &cm->cmsg_type); |
---|
311 | 320 | if (!err) |
---|
312 | 321 | err = put_user(cmlen, &cm->cmsg_len); |
---|
313 | 322 | if (!err) { |
---|
314 | | - cmlen = CMSG_SPACE(i*sizeof(int)); |
---|
| 323 | + cmlen = CMSG_SPACE(i * sizeof(int)); |
---|
315 | 324 | if (msg->msg_controllen < cmlen) |
---|
316 | 325 | cmlen = msg->msg_controllen; |
---|
317 | 326 | msg->msg_control += cmlen; |
---|
318 | 327 | msg->msg_controllen -= cmlen; |
---|
319 | 328 | } |
---|
320 | 329 | } |
---|
321 | | - if (i < fdnum || (fdnum && fdmax <= 0)) |
---|
| 330 | + |
---|
| 331 | + if (i < scm->fp->count || (scm->fp->count && fdmax <= 0)) |
---|
322 | 332 | msg->msg_flags |= MSG_CTRUNC; |
---|
323 | 333 | |
---|
324 | 334 | /* |
---|
325 | | - * All of the files that fit in the message have had their |
---|
326 | | - * usage counts incremented, so we just free the list. |
---|
| 335 | + * All of the files that fit in the message have had their usage counts |
---|
| 336 | + * incremented, so we just free the list. |
---|
327 | 337 | */ |
---|
328 | 338 | __scm_destroy(scm); |
---|
329 | 339 | } |
---|