xref: /kernel/linux/linux-6.6/include/net/scm.h (revision 62306a36)
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef __LINUX_NET_SCM_H
3#define __LINUX_NET_SCM_H
4
5#include <linux/limits.h>
6#include <linux/net.h>
7#include <linux/cred.h>
8#include <linux/security.h>
9#include <linux/pid.h>
10#include <linux/nsproxy.h>
11#include <linux/sched/signal.h>
12#include <net/compat.h>
13
14/* Well, we should have at least one descriptor open
15 * to accept passed FDs 8)
16 */
17#define SCM_MAX_FD	253
18
19struct scm_creds {
20	u32	pid;
21	kuid_t	uid;
22	kgid_t	gid;
23};
24
25struct scm_fp_list {
26	short			count;
27	short			max;
28	struct user_struct	*user;
29	struct file		*fp[SCM_MAX_FD];
30};
31
32struct scm_cookie {
33	struct pid		*pid;		/* Skb credentials */
34	struct scm_fp_list	*fp;		/* Passed files		*/
35	struct scm_creds	creds;		/* Skb credentials	*/
36#ifdef CONFIG_SECURITY_NETWORK
37	u32			secid;		/* Passed security ID 	*/
38#endif
39};
40
41void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm);
42void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm);
43int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm);
44void __scm_destroy(struct scm_cookie *scm);
45struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl);
46
47#ifdef CONFIG_SECURITY_NETWORK
48static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_cookie *scm)
49{
50	security_socket_getpeersec_dgram(sock, NULL, &scm->secid);
51}
52#else
53static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_cookie *scm)
54{ }
55#endif /* CONFIG_SECURITY_NETWORK */
56
57static __inline__ void scm_set_cred(struct scm_cookie *scm,
58				    struct pid *pid, kuid_t uid, kgid_t gid)
59{
60	scm->pid  = get_pid(pid);
61	scm->creds.pid = pid_vnr(pid);
62	scm->creds.uid = uid;
63	scm->creds.gid = gid;
64}
65
66static __inline__ void scm_destroy_cred(struct scm_cookie *scm)
67{
68	put_pid(scm->pid);
69	scm->pid  = NULL;
70}
71
72static __inline__ void scm_destroy(struct scm_cookie *scm)
73{
74	scm_destroy_cred(scm);
75	if (scm->fp)
76		__scm_destroy(scm);
77}
78
79static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
80			       struct scm_cookie *scm, bool forcecreds)
81{
82	memset(scm, 0, sizeof(*scm));
83	scm->creds.uid = INVALID_UID;
84	scm->creds.gid = INVALID_GID;
85	if (forcecreds)
86		scm_set_cred(scm, task_tgid(current), current_uid(), current_gid());
87	unix_get_peersec_dgram(sock, scm);
88	if (msg->msg_controllen <= 0)
89		return 0;
90	return __scm_send(sock, msg, scm);
91}
92
93#ifdef CONFIG_SECURITY_NETWORK
94static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
95{
96	char *secdata;
97	u32 seclen;
98	int err;
99
100	if (test_bit(SOCK_PASSSEC, &sock->flags)) {
101		err = security_secid_to_secctx(scm->secid, &secdata, &seclen);
102
103		if (!err) {
104			put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, seclen, secdata);
105			security_release_secctx(secdata, seclen);
106		}
107	}
108}
109
110static inline bool scm_has_secdata(struct socket *sock)
111{
112	return test_bit(SOCK_PASSSEC, &sock->flags);
113}
114#else
115static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)
116{ }
117
118static inline bool scm_has_secdata(struct socket *sock)
119{
120	return false;
121}
122#endif /* CONFIG_SECURITY_NETWORK */
123
124static __inline__ void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
125{
126	struct file *pidfd_file = NULL;
127	int len, pidfd;
128
129	/* put_cmsg() doesn't return an error if CMSG is truncated,
130	 * that's why we need to opencode these checks here.
131	 */
132	if (msg->msg_flags & MSG_CMSG_COMPAT)
133		len = sizeof(struct compat_cmsghdr) + sizeof(int);
134	else
135		len = sizeof(struct cmsghdr) + sizeof(int);
136
137	if (msg->msg_controllen < len) {
138		msg->msg_flags |= MSG_CTRUNC;
139		return;
140	}
141
142	if (!scm->pid)
143		return;
144
145	pidfd = pidfd_prepare(scm->pid, 0, &pidfd_file);
146
147	if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
148		if (pidfd_file) {
149			put_unused_fd(pidfd);
150			fput(pidfd_file);
151		}
152
153		return;
154	}
155
156	if (pidfd_file)
157		fd_install(pidfd, pidfd_file);
158}
159
160static inline bool __scm_recv_common(struct socket *sock, struct msghdr *msg,
161				     struct scm_cookie *scm, int flags)
162{
163	if (!msg->msg_control) {
164		if (test_bit(SOCK_PASSCRED, &sock->flags) ||
165		    test_bit(SOCK_PASSPIDFD, &sock->flags) ||
166		    scm->fp || scm_has_secdata(sock))
167			msg->msg_flags |= MSG_CTRUNC;
168		scm_destroy(scm);
169		return false;
170	}
171
172	if (test_bit(SOCK_PASSCRED, &sock->flags)) {
173		struct user_namespace *current_ns = current_user_ns();
174		struct ucred ucreds = {
175			.pid = scm->creds.pid,
176			.uid = from_kuid_munged(current_ns, scm->creds.uid),
177			.gid = from_kgid_munged(current_ns, scm->creds.gid),
178		};
179		put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
180	}
181
182	scm_passec(sock, msg, scm);
183
184	if (scm->fp)
185		scm_detach_fds(msg, scm);
186
187	return true;
188}
189
190static inline void scm_recv(struct socket *sock, struct msghdr *msg,
191			    struct scm_cookie *scm, int flags)
192{
193	if (!__scm_recv_common(sock, msg, scm, flags))
194		return;
195
196	scm_destroy_cred(scm);
197}
198
199static inline void scm_recv_unix(struct socket *sock, struct msghdr *msg,
200				 struct scm_cookie *scm, int flags)
201{
202	if (!__scm_recv_common(sock, msg, scm, flags))
203		return;
204
205	if (test_bit(SOCK_PASSPIDFD, &sock->flags))
206		scm_pidfd_recv(msg, scm);
207
208	scm_destroy_cred(scm);
209}
210
211#endif /* __LINUX_NET_SCM_H */
212
213