scm: Move scm_recv() from scm.h to scm.c.

scm_recv() has been placed in scm.h since the pre-git era for no
particular reason (I think), which makes the file really fragile.

For example, when you move SOCK_PASSCRED from include/linux/net.h to
enum sock_flags in include/net/sock.h, you will see weird build failure
due to terrible dependency.

To avoid the build failure in the future, let's move scm_recv(_unix())?
and its callees to scm.c.

Note that only scm_recv() needs to be exported for Bluetooth.

scm_send() should be moved to scm.c too, but I'll revisit later.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Kuniyuki Iwashima 2025-05-19 13:57:54 -07:00 committed by David S. Miller
parent 3041bbbeb4
commit 38b95d588f
2 changed files with 127 additions and 117 deletions

View File

@ -102,123 +102,10 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
return __scm_send(sock, msg, scm);
}
#ifdef CONFIG_SECURITY_NETWORK
static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
{
struct lsm_context ctx;
int err;
if (test_bit(SOCK_PASSSEC, &sock->flags)) {
err = security_secid_to_secctx(scm->secid, &ctx);
if (err >= 0) {
put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, ctx.len,
ctx.context);
security_release_secctx(&ctx);
}
}
}
static inline bool scm_has_secdata(struct socket *sock)
{
return test_bit(SOCK_PASSSEC, &sock->flags);
}
#else
static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
{ }
static inline bool scm_has_secdata(struct socket *sock)
{
return false;
}
#endif /* CONFIG_SECURITY_NETWORK */
static __inline__ void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
{
struct file *pidfd_file = NULL;
int len, pidfd;
/* put_cmsg() doesn't return an error if CMSG is truncated,
* that's why we need to opencode these checks here.
*/
if (msg->msg_flags & MSG_CMSG_COMPAT)
len = sizeof(struct compat_cmsghdr) + sizeof(int);
else
len = sizeof(struct cmsghdr) + sizeof(int);
if (msg->msg_controllen < len) {
msg->msg_flags |= MSG_CTRUNC;
return;
}
if (!scm->pid)
return;
pidfd = pidfd_prepare(scm->pid, 0, &pidfd_file);
if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
if (pidfd_file) {
put_unused_fd(pidfd);
fput(pidfd_file);
}
return;
}
if (pidfd_file)
fd_install(pidfd, pidfd_file);
}
static inline bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
if (!msg->msg_control) {
if (test_bit(SOCK_PASSCRED, &sock->flags) ||
test_bit(SOCK_PASSPIDFD, &sock->flags) ||
scm->fp || scm_has_secdata(sock))
msg->msg_flags |= MSG_CTRUNC;
scm_destroy(scm);
return false;
}
if (test_bit(SOCK_PASSCRED, &sock->flags)) {
struct user_namespace *current_ns = current_user_ns();
struct ucred ucreds = {
.pid = scm->creds.pid,
.uid = from_kuid_munged(current_ns, scm->creds.uid),
.gid = from_kgid_munged(current_ns, scm->creds.gid),
};
put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
}
scm_passec(sock, msg, scm);
if (scm->fp)
scm_detach_fds(msg, scm);
return true;
}
static inline void scm_recv(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
if (!__scm_recv_common(sock, msg, scm, flags))
return;
scm_destroy_cred(scm);
}
static inline void scm_recv_unix(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
if (!__scm_recv_common(sock, msg, scm, flags))
return;
if (test_bit(SOCK_PASSPIDFD, &sock->flags))
scm_pidfd_recv(msg, scm);
scm_destroy_cred(scm);
}
void scm_recv(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags);
void scm_recv_unix(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags);
static inline int scm_recv_one_fd(struct file *f, int __user *ufd,
unsigned int flags)

View File

@ -404,3 +404,126 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
return new_fpl;
}
EXPORT_SYMBOL(scm_fp_dup);
#ifdef CONFIG_SECURITY_NETWORK
static void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
{
struct lsm_context ctx;
int err;
if (test_bit(SOCK_PASSSEC, &sock->flags)) {
err = security_secid_to_secctx(scm->secid, &ctx);
if (err >= 0) {
put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, ctx.len,
ctx.context);
security_release_secctx(&ctx);
}
}
}
static bool scm_has_secdata(struct socket *sock)
{
return test_bit(SOCK_PASSSEC, &sock->flags);
}
#else
static void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
{
}
static bool scm_has_secdata(struct socket *sock)
{
return false;
}
#endif
static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
{
struct file *pidfd_file = NULL;
int len, pidfd;
/* put_cmsg() doesn't return an error if CMSG is truncated,
* that's why we need to opencode these checks here.
*/
if (msg->msg_flags & MSG_CMSG_COMPAT)
len = sizeof(struct compat_cmsghdr) + sizeof(int);
else
len = sizeof(struct cmsghdr) + sizeof(int);
if (msg->msg_controllen < len) {
msg->msg_flags |= MSG_CTRUNC;
return;
}
if (!scm->pid)
return;
pidfd = pidfd_prepare(scm->pid, 0, &pidfd_file);
if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
if (pidfd_file) {
put_unused_fd(pidfd);
fput(pidfd_file);
}
return;
}
if (pidfd_file)
fd_install(pidfd, pidfd_file);
}
static bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
if (!msg->msg_control) {
if (test_bit(SOCK_PASSCRED, &sock->flags) ||
test_bit(SOCK_PASSPIDFD, &sock->flags) ||
scm->fp || scm_has_secdata(sock))
msg->msg_flags |= MSG_CTRUNC;
scm_destroy(scm);
return false;
}
if (test_bit(SOCK_PASSCRED, &sock->flags)) {
struct user_namespace *current_ns = current_user_ns();
struct ucred ucreds = {
.pid = scm->creds.pid,
.uid = from_kuid_munged(current_ns, scm->creds.uid),
.gid = from_kgid_munged(current_ns, scm->creds.gid),
};
put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
}
scm_passec(sock, msg, scm);
if (scm->fp)
scm_detach_fds(msg, scm);
return true;
}
void scm_recv(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
if (!__scm_recv_common(sock, msg, scm, flags))
return;
scm_destroy_cred(scm);
}
EXPORT_SYMBOL(scm_recv);
void scm_recv_unix(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
if (!__scm_recv_common(sock, msg, scm, flags))
return;
if (test_bit(SOCK_PASSPIDFD, &sock->flags))
scm_pidfd_recv(msg, scm);
scm_destroy_cred(scm);
}