Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/core/scm.c
15109 views
1
/* scm.c - Socket level control messages processing.
2
*
3
* Author: Alexey Kuznetsov, <[email protected]>
4
* 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
*/
11
12
#include <linux/module.h>
13
#include <linux/signal.h>
14
#include <linux/capability.h>
15
#include <linux/errno.h>
16
#include <linux/sched.h>
17
#include <linux/mm.h>
18
#include <linux/kernel.h>
19
#include <linux/stat.h>
20
#include <linux/socket.h>
21
#include <linux/file.h>
22
#include <linux/fcntl.h>
23
#include <linux/net.h>
24
#include <linux/interrupt.h>
25
#include <linux/netdevice.h>
26
#include <linux/security.h>
27
#include <linux/pid.h>
28
#include <linux/nsproxy.h>
29
#include <linux/slab.h>
30
31
#include <asm/system.h>
32
#include <asm/uaccess.h>
33
34
#include <net/protocol.h>
35
#include <linux/skbuff.h>
36
#include <net/sock.h>
37
#include <net/compat.h>
38
#include <net/scm.h>
39
40
41
/*
42
* Only allow a user to send credentials, that they could set with
43
* setu(g)id.
44
*/
45
46
static __inline__ int scm_check_creds(struct ucred *creds)
47
{
48
const struct cred *cred = current_cred();
49
50
if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) &&
51
((creds->uid == cred->uid || creds->uid == cred->euid ||
52
creds->uid == cred->suid) || capable(CAP_SETUID)) &&
53
((creds->gid == cred->gid || creds->gid == cred->egid ||
54
creds->gid == cred->sgid) || capable(CAP_SETGID))) {
55
return 0;
56
}
57
return -EPERM;
58
}
59
60
static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
61
{
62
int *fdp = (int*)CMSG_DATA(cmsg);
63
struct scm_fp_list *fpl = *fplp;
64
struct file **fpp;
65
int i, num;
66
67
num = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))/sizeof(int);
68
69
if (num <= 0)
70
return 0;
71
72
if (num > SCM_MAX_FD)
73
return -EINVAL;
74
75
if (!fpl)
76
{
77
fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
78
if (!fpl)
79
return -ENOMEM;
80
*fplp = fpl;
81
fpl->count = 0;
82
fpl->max = SCM_MAX_FD;
83
}
84
fpp = &fpl->fp[fpl->count];
85
86
if (fpl->count + num > fpl->max)
87
return -EINVAL;
88
89
/*
90
* Verify the descriptors and increment the usage count.
91
*/
92
93
for (i=0; i< num; i++)
94
{
95
int fd = fdp[i];
96
struct file *file;
97
98
if (fd < 0 || !(file = fget_raw(fd)))
99
return -EBADF;
100
*fpp++ = file;
101
fpl->count++;
102
}
103
return num;
104
}
105
106
void __scm_destroy(struct scm_cookie *scm)
107
{
108
struct scm_fp_list *fpl = scm->fp;
109
int i;
110
111
if (fpl) {
112
scm->fp = NULL;
113
if (current->scm_work_list) {
114
list_add_tail(&fpl->list, current->scm_work_list);
115
} else {
116
LIST_HEAD(work_list);
117
118
current->scm_work_list = &work_list;
119
120
list_add(&fpl->list, &work_list);
121
while (!list_empty(&work_list)) {
122
fpl = list_first_entry(&work_list, struct scm_fp_list, list);
123
124
list_del(&fpl->list);
125
for (i=fpl->count-1; i>=0; i--)
126
fput(fpl->fp[i]);
127
kfree(fpl);
128
}
129
130
current->scm_work_list = NULL;
131
}
132
}
133
}
134
EXPORT_SYMBOL(__scm_destroy);
135
136
int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
137
{
138
struct cmsghdr *cmsg;
139
int err;
140
141
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
142
{
143
err = -EINVAL;
144
145
/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
146
/* The first check was omitted in <= 2.2.5. The reasoning was
147
that parser checks cmsg_len in any case, so that
148
additional check would be work duplication.
149
But if cmsg_level is not SOL_SOCKET, we do not check
150
for too short ancillary data object at all! Oops.
151
OK, let's add it...
152
*/
153
if (!CMSG_OK(msg, cmsg))
154
goto error;
155
156
if (cmsg->cmsg_level != SOL_SOCKET)
157
continue;
158
159
switch (cmsg->cmsg_type)
160
{
161
case SCM_RIGHTS:
162
if (!sock->ops || sock->ops->family != PF_UNIX)
163
goto error;
164
err=scm_fp_copy(cmsg, &p->fp);
165
if (err<0)
166
goto error;
167
break;
168
case SCM_CREDENTIALS:
169
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
170
goto error;
171
memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred));
172
err = scm_check_creds(&p->creds);
173
if (err)
174
goto error;
175
176
if (pid_vnr(p->pid) != p->creds.pid) {
177
struct pid *pid;
178
err = -ESRCH;
179
pid = find_get_pid(p->creds.pid);
180
if (!pid)
181
goto error;
182
put_pid(p->pid);
183
p->pid = pid;
184
}
185
186
if ((p->cred->euid != p->creds.uid) ||
187
(p->cred->egid != p->creds.gid)) {
188
struct cred *cred;
189
err = -ENOMEM;
190
cred = prepare_creds();
191
if (!cred)
192
goto error;
193
194
cred->uid = cred->euid = p->creds.uid;
195
cred->gid = cred->egid = p->creds.uid;
196
put_cred(p->cred);
197
p->cred = cred;
198
}
199
break;
200
default:
201
goto error;
202
}
203
}
204
205
if (p->fp && !p->fp->count)
206
{
207
kfree(p->fp);
208
p->fp = NULL;
209
}
210
return 0;
211
212
error:
213
scm_destroy(p);
214
return err;
215
}
216
EXPORT_SYMBOL(__scm_send);
217
218
int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
219
{
220
struct cmsghdr __user *cm
221
= (__force struct cmsghdr __user *)msg->msg_control;
222
struct cmsghdr cmhdr;
223
int cmlen = CMSG_LEN(len);
224
int err;
225
226
if (MSG_CMSG_COMPAT & msg->msg_flags)
227
return put_cmsg_compat(msg, level, type, len, data);
228
229
if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
230
msg->msg_flags |= MSG_CTRUNC;
231
return 0; /* XXX: return error? check spec. */
232
}
233
if (msg->msg_controllen < cmlen) {
234
msg->msg_flags |= MSG_CTRUNC;
235
cmlen = msg->msg_controllen;
236
}
237
cmhdr.cmsg_level = level;
238
cmhdr.cmsg_type = type;
239
cmhdr.cmsg_len = cmlen;
240
241
err = -EFAULT;
242
if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
243
goto out;
244
if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
245
goto out;
246
cmlen = CMSG_SPACE(len);
247
if (msg->msg_controllen < cmlen)
248
cmlen = msg->msg_controllen;
249
msg->msg_control += cmlen;
250
msg->msg_controllen -= cmlen;
251
err = 0;
252
out:
253
return err;
254
}
255
EXPORT_SYMBOL(put_cmsg);
256
257
void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
258
{
259
struct cmsghdr __user *cm
260
= (__force struct cmsghdr __user*)msg->msg_control;
261
262
int fdmax = 0;
263
int fdnum = scm->fp->count;
264
struct file **fp = scm->fp->fp;
265
int __user *cmfptr;
266
int err = 0, i;
267
268
if (MSG_CMSG_COMPAT & msg->msg_flags) {
269
scm_detach_fds_compat(msg, scm);
270
return;
271
}
272
273
if (msg->msg_controllen > sizeof(struct cmsghdr))
274
fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr))
275
/ sizeof(int));
276
277
if (fdnum < fdmax)
278
fdmax = fdnum;
279
280
for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax;
281
i++, cmfptr++)
282
{
283
int new_fd;
284
err = security_file_receive(fp[i]);
285
if (err)
286
break;
287
err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & msg->msg_flags
288
? O_CLOEXEC : 0);
289
if (err < 0)
290
break;
291
new_fd = err;
292
err = put_user(new_fd, cmfptr);
293
if (err) {
294
put_unused_fd(new_fd);
295
break;
296
}
297
/* Bump the usage count and install the file. */
298
get_file(fp[i]);
299
fd_install(new_fd, fp[i]);
300
}
301
302
if (i > 0)
303
{
304
int cmlen = CMSG_LEN(i*sizeof(int));
305
err = put_user(SOL_SOCKET, &cm->cmsg_level);
306
if (!err)
307
err = put_user(SCM_RIGHTS, &cm->cmsg_type);
308
if (!err)
309
err = put_user(cmlen, &cm->cmsg_len);
310
if (!err) {
311
cmlen = CMSG_SPACE(i*sizeof(int));
312
msg->msg_control += cmlen;
313
msg->msg_controllen -= cmlen;
314
}
315
}
316
if (i < fdnum || (fdnum && fdmax <= 0))
317
msg->msg_flags |= MSG_CTRUNC;
318
319
/*
320
* All of the files that fit in the message have had their
321
* usage counts incremented, so we just free the list.
322
*/
323
__scm_destroy(scm);
324
}
325
EXPORT_SYMBOL(scm_detach_fds);
326
327
struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
328
{
329
struct scm_fp_list *new_fpl;
330
int i;
331
332
if (!fpl)
333
return NULL;
334
335
new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]),
336
GFP_KERNEL);
337
if (new_fpl) {
338
for (i = 0; i < fpl->count; i++)
339
get_file(fpl->fp[i]);
340
new_fpl->max = new_fpl->count;
341
}
342
return new_fpl;
343
}
344
EXPORT_SYMBOL(scm_fp_dup);
345
346