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, secid: &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(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, pid: 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(secid: scm->secid, secdata: &secdata, seclen: &seclen);
102
103 if (!err) {
104 put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, len: seclen, data: 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(pid: scm->pid, flags: 0, ret: &pidfd_file);
146
147 if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, len: sizeof(int), data: &pidfd)) {
148 if (pidfd_file) {
149 put_unused_fd(fd: pidfd);
150 fput(pidfd_file);
151 }
152
153 return;
154 }
155
156 if (pidfd_file)
157 fd_install(fd: pidfd, file: 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(to: current_ns, uid: scm->creds.uid),
177 .gid = from_kgid_munged(to: current_ns, gid: scm->creds.gid),
178 };
179 put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, len: sizeof(ucreds), data: &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

source code of linux/include/net/scm.h