| .. | .. |
|---|
| 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 | } |
|---|