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