| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
#include <linux/kernel.h> |
|---|
| 15 |
#include <linux/fs.h> |
|---|
| 16 |
#include <linux/sched.h> |
|---|
| 17 |
#include <linux/types.h> |
|---|
| 18 |
#include <linux/file.h> |
|---|
| 19 |
#include <linux/icmpv6.h> |
|---|
| 20 |
#include <linux/socket.h> |
|---|
| 21 |
#include <linux/syscalls.h> |
|---|
| 22 |
#include <linux/filter.h> |
|---|
| 23 |
#include <linux/compat.h> |
|---|
| 24 |
#include <linux/netfilter_ipv4/ip_tables.h> |
|---|
| 25 |
|
|---|
| 26 |
#include <net/scm.h> |
|---|
| 27 |
#include <net/sock.h> |
|---|
| 28 |
#include <asm/uaccess.h> |
|---|
| 29 |
#include <net/compat.h> |
|---|
| 30 |
|
|---|
| 31 |
static inline int iov_from_user_compat_to_kern(struct iovec *kiov, |
|---|
| 32 |
struct compat_iovec __user *uiov32, |
|---|
| 33 |
int niov) |
|---|
| 34 |
{ |
|---|
| 35 |
int tot_len = 0; |
|---|
| 36 |
|
|---|
| 37 |
while(niov > 0) { |
|---|
| 38 |
compat_uptr_t buf; |
|---|
| 39 |
compat_size_t len; |
|---|
| 40 |
|
|---|
| 41 |
if(get_user(len, &uiov32->iov_len) || |
|---|
| 42 |
get_user(buf, &uiov32->iov_base)) { |
|---|
| 43 |
tot_len = -EFAULT; |
|---|
| 44 |
break; |
|---|
| 45 |
} |
|---|
| 46 |
tot_len += len; |
|---|
| 47 |
kiov->iov_base = compat_ptr(buf); |
|---|
| 48 |
kiov->iov_len = (__kernel_size_t) len; |
|---|
| 49 |
uiov32++; |
|---|
| 50 |
kiov++; |
|---|
| 51 |
niov--; |
|---|
| 52 |
} |
|---|
| 53 |
return tot_len; |
|---|
| 54 |
} |
|---|
| 55 |
|
|---|
| 56 |
int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) |
|---|
| 57 |
{ |
|---|
| 58 |
compat_uptr_t tmp1, tmp2, tmp3; |
|---|
| 59 |
|
|---|
| 60 |
if (!access_ok(VERIFY_READ, umsg, sizeof(*umsg)) || |
|---|
| 61 |
__get_user(tmp1, &umsg->msg_name) || |
|---|
| 62 |
__get_user(kmsg->msg_namelen, &umsg->msg_namelen) || |
|---|
| 63 |
__get_user(tmp2, &umsg->msg_iov) || |
|---|
| 64 |
__get_user(kmsg->msg_iovlen, &umsg->msg_iovlen) || |
|---|
| 65 |
__get_user(tmp3, &umsg->msg_control) || |
|---|
| 66 |
__get_user(kmsg->msg_controllen, &umsg->msg_controllen) || |
|---|
| 67 |
__get_user(kmsg->msg_flags, &umsg->msg_flags)) |
|---|
| 68 |
return -EFAULT; |
|---|
| 69 |
kmsg->msg_name = compat_ptr(tmp1); |
|---|
| 70 |
kmsg->msg_iov = compat_ptr(tmp2); |
|---|
| 71 |
kmsg->msg_control = compat_ptr(tmp3); |
|---|
| 72 |
return 0; |
|---|
| 73 |
} |
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, |
|---|
| 77 |
char *kern_address, int mode) |
|---|
| 78 |
{ |
|---|
| 79 |
int tot_len; |
|---|
| 80 |
|
|---|
| 81 |
if(kern_msg->msg_namelen) { |
|---|
| 82 |
if(mode==VERIFY_READ) { |
|---|
| 83 |
int err = move_addr_to_kernel(kern_msg->msg_name, |
|---|
| 84 |
kern_msg->msg_namelen, |
|---|
| 85 |
kern_address); |
|---|
| 86 |
if(err < 0) |
|---|
| 87 |
return err; |
|---|
| 88 |
} |
|---|
| 89 |
kern_msg->msg_name = kern_address; |
|---|
| 90 |
} else |
|---|
| 91 |
kern_msg->msg_name = NULL; |
|---|
| 92 |
|
|---|
| 93 |
if(kern_msg->msg_iovlen > UIO_FASTIOV) { |
|---|
| 94 |
kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), |
|---|
| 95 |
GFP_KERNEL); |
|---|
| 96 |
if(!kern_iov) |
|---|
| 97 |
return -ENOMEM; |
|---|
| 98 |
} |
|---|
| 99 |
|
|---|
| 100 |
tot_len = iov_from_user_compat_to_kern(kern_iov, |
|---|
| 101 |
(struct compat_iovec __user *)kern_msg->msg_iov, |
|---|
| 102 |
kern_msg->msg_iovlen); |
|---|
| 103 |
if(tot_len >= 0) |
|---|
| 104 |
kern_msg->msg_iov = kern_iov; |
|---|
| 105 |
else if(kern_msg->msg_iovlen > UIO_FASTIOV) |
|---|
| 106 |
kfree(kern_iov); |
|---|
| 107 |
|
|---|
| 108 |
return tot_len; |
|---|
| 109 |
} |
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
#define CMSG_COMPAT_ALIGN(len) ALIGN((len), sizeof(s32)) |
|---|
| 113 |
|
|---|
| 114 |
#define CMSG_COMPAT_DATA(cmsg) \ |
|---|
| 115 |
((void __user *)((char __user *)(cmsg) + CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)))) |
|---|
| 116 |
#define CMSG_COMPAT_SPACE(len) \ |
|---|
| 117 |
(CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)) + CMSG_COMPAT_ALIGN(len)) |
|---|
| 118 |
#define CMSG_COMPAT_LEN(len) \ |
|---|
| 119 |
(CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)) + (len)) |
|---|
| 120 |
|
|---|
| 121 |
#define CMSG_COMPAT_FIRSTHDR(msg) \ |
|---|
| 122 |
(((msg)->msg_controllen) >= sizeof(struct compat_cmsghdr) ? \ |
|---|
| 123 |
(struct compat_cmsghdr __user *)((msg)->msg_control) : \ |
|---|
| 124 |
(struct compat_cmsghdr __user *)NULL) |
|---|
| 125 |
|
|---|
| 126 |
static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *msg, |
|---|
| 127 |
struct compat_cmsghdr __user *cmsg, int cmsg_len) |
|---|
| 128 |
{ |
|---|
| 129 |
char __user *ptr = (char __user *)cmsg + CMSG_COMPAT_ALIGN(cmsg_len); |
|---|
| 130 |
if ((unsigned long)(ptr + 1 - (char __user *)msg->msg_control) > |
|---|
| 131 |
msg->msg_controllen) |
|---|
| 132 |
return NULL; |
|---|
| 133 |
return (struct compat_cmsghdr __user *)ptr; |
|---|
| 134 |
} |
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
|
|---|
| 139 |
|
|---|
| 140 |
int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, |
|---|
| 141 |
unsigned char *stackbuf, int stackbuf_size) |
|---|
| 142 |
{ |
|---|
| 143 |
struct compat_cmsghdr __user *ucmsg; |
|---|
| 144 |
struct cmsghdr *kcmsg, *kcmsg_base; |
|---|
| 145 |
compat_size_t ucmlen; |
|---|
| 146 |
__kernel_size_t kcmlen, tmp; |
|---|
| 147 |
|
|---|
| 148 |
kcmlen = 0; |
|---|
| 149 |
kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; |
|---|
| 150 |
ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); |
|---|
| 151 |
while(ucmsg != NULL) { |
|---|
| 152 |
if(get_user(ucmlen, &ucmsg->cmsg_len)) |
|---|
| 153 |
return -EFAULT; |
|---|
| 154 |
|
|---|
| 155 |
|
|---|
| 156 |
if(CMSG_COMPAT_ALIGN(ucmlen) < |
|---|
| 157 |
CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr))) |
|---|
| 158 |
return -EINVAL; |
|---|
| 159 |
if((unsigned long)(((char __user *)ucmsg - (char __user *)kmsg->msg_control) |
|---|
| 160 |
+ ucmlen) > kmsg->msg_controllen) |
|---|
| 161 |
return -EINVAL; |
|---|
| 162 |
|
|---|
| 163 |
tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + |
|---|
| 164 |
CMSG_ALIGN(sizeof(struct cmsghdr))); |
|---|
| 165 |
kcmlen += tmp; |
|---|
| 166 |
ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); |
|---|
| 167 |
} |
|---|
| 168 |
if(kcmlen == 0) |
|---|
| 169 |
return -EINVAL; |
|---|
| 170 |
|
|---|
| 171 |
|
|---|
| 172 |
|
|---|
| 173 |
|
|---|
| 174 |
|
|---|
| 175 |
|
|---|
| 176 |
if(kcmlen > stackbuf_size) |
|---|
| 177 |
kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); |
|---|
| 178 |
if(kcmsg == NULL) |
|---|
| 179 |
return -ENOBUFS; |
|---|
| 180 |
|
|---|
| 181 |
|
|---|
| 182 |
memset(kcmsg, 0, kcmlen); |
|---|
| 183 |
ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); |
|---|
| 184 |
while(ucmsg != NULL) { |
|---|
| 185 |
__get_user(ucmlen, &ucmsg->cmsg_len); |
|---|
| 186 |
tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + |
|---|
| 187 |
CMSG_ALIGN(sizeof(struct cmsghdr))); |
|---|
| 188 |
kcmsg->cmsg_len = tmp; |
|---|
| 189 |
__get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); |
|---|
| 190 |
__get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); |
|---|
| 191 |
|
|---|
| 192 |
|
|---|
| 193 |
if(copy_from_user(CMSG_DATA(kcmsg), |
|---|
| 194 |
CMSG_COMPAT_DATA(ucmsg), |
|---|
| 195 |
(ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) |
|---|
| 196 |
goto out_free_efault; |
|---|
| 197 |
|
|---|
| 198 |
|
|---|
| 199 |
kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); |
|---|
| 200 |
ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); |
|---|
| 201 |
} |
|---|
| 202 |
|
|---|
| 203 |
|
|---|
| 204 |
kmsg->msg_control = kcmsg_base; |
|---|
| 205 |
kmsg->msg_controllen = kcmlen; |
|---|
| 206 |
return 0; |
|---|
| 207 |
|
|---|
| 208 |
out_free_efault: |
|---|
| 209 |
if(kcmsg_base != (struct cmsghdr *)stackbuf) |
|---|
| 210 |
kfree(kcmsg_base); |
|---|
| 211 |
return -EFAULT; |
|---|
| 212 |
} |
|---|
| 213 |
|
|---|
| 214 |
int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) |
|---|
| 215 |
{ |
|---|
| 216 |
struct compat_timeval ctv; |
|---|
| 217 |
struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; |
|---|
| 218 |
struct compat_cmsghdr cmhdr; |
|---|
| 219 |
int cmlen; |
|---|
| 220 |
|
|---|
| 221 |
if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { |
|---|
| 222 |
kmsg->msg_flags |= MSG_CTRUNC; |
|---|
| 223 |
return 0; |
|---|
| 224 |
} |
|---|
| 225 |
|
|---|
| 226 |
if (level == SOL_SOCKET && type == SO_TIMESTAMP) { |
|---|
| 227 |
struct timeval *tv = (struct timeval *)data; |
|---|
| 228 |
ctv.tv_sec = tv->tv_sec; |
|---|
| 229 |
ctv.tv_usec = tv->tv_usec; |
|---|
| 230 |
data = &ctv; |
|---|
| 231 |
len = sizeof(struct compat_timeval); |
|---|
| 232 |
} |
|---|
| 233 |
|
|---|
| 234 |
cmlen = CMSG_COMPAT_LEN(len); |
|---|
| 235 |
if(kmsg->msg_controllen < cmlen) { |
|---|
| 236 |
kmsg->msg_flags |= MSG_CTRUNC; |
|---|
| 237 |
cmlen = kmsg->msg_controllen; |
|---|
| 238 |
} |
|---|
| 239 |
cmhdr.cmsg_level = level; |
|---|
| 240 |
cmhdr.cmsg_type = type; |
|---|
| 241 |
cmhdr.cmsg_len = cmlen; |
|---|
| 242 |
|
|---|
| 243 |
if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) |
|---|
| 244 |
return -EFAULT; |
|---|
| 245 |
if(copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr))) |
|---|
| 246 |
return -EFAULT; |
|---|
| 247 |
cmlen = CMSG_COMPAT_SPACE(len); |
|---|
| 248 |
kmsg->msg_control += cmlen; |
|---|
| 249 |
kmsg->msg_controllen -= cmlen; |
|---|
| 250 |
return 0; |
|---|
| 251 |
} |
|---|
| 252 |
|
|---|
| 253 |
void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) |
|---|
| 254 |
{ |
|---|
| 255 |
struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; |
|---|
| 256 |
int fdmax = (kmsg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int); |
|---|
| 257 |
int fdnum = scm->fp->count; |
|---|
| 258 |
struct file **fp = scm->fp->fp; |
|---|
| 259 |
int __user *cmfptr; |
|---|
| 260 |
int err = 0, i; |
|---|
| 261 |
|
|---|
| 262 |
if (fdnum < fdmax) |
|---|
| 263 |
fdmax = fdnum; |
|---|
| 264 |
|
|---|
| 265 |
for (i = 0, cmfptr = (int __user *) CMSG_COMPAT_DATA(cm); i < fdmax; i++, cmfptr++) { |
|---|
| 266 |
int new_fd; |
|---|
| 267 |
err = get_unused_fd(); |
|---|
| 268 |
if (err < 0) |
|---|
| 269 |
break; |
|---|
| 270 |
new_fd = err; |
|---|
| 271 |
err = put_user(new_fd, cmfptr); |
|---|
| 272 |
if (err) { |
|---|
| 273 |
put_unused_fd(new_fd); |
|---|
| 274 |
break; |
|---|
| 275 |
} |
|---|
| 276 |
|
|---|
| 277 |
get_file(fp[i]); |
|---|
| 278 |
fd_install(new_fd, fp[i]); |
|---|
| 279 |
} |
|---|
| 280 |
|
|---|
| 281 |
if (i > 0) { |
|---|
| 282 |
int cmlen = CMSG_COMPAT_LEN(i * sizeof(int)); |
|---|
| 283 |
if (!err) |
|---|
| 284 |
err = put_user(SOL_SOCKET, &cm->cmsg_level); |
|---|
| 285 |
if (!err) |
|---|
| 286 |
err = put_user(SCM_RIGHTS, &cm->cmsg_type); |
|---|
| 287 |
if (!err) |
|---|
| 288 |
err = put_user(cmlen, &cm->cmsg_len); |
|---|
| 289 |
if (!err) { |
|---|
| 290 |
cmlen = CMSG_COMPAT_SPACE(i * sizeof(int)); |
|---|
| 291 |
kmsg->msg_control += cmlen; |
|---|
| 292 |
kmsg->msg_controllen -= cmlen; |
|---|
| 293 |
} |
|---|
| 294 |
} |
|---|
| 295 |
if (i < fdnum) |
|---|
| 296 |
kmsg->msg_flags |= MSG_CTRUNC; |
|---|
| 297 |
|
|---|
| 298 |
|
|---|
| 299 |
|
|---|
| 300 |
|
|---|
| 301 |
|
|---|
| 302 |
__scm_destroy(scm); |
|---|
| 303 |
} |
|---|
| 304 |
|
|---|
| 305 |
|
|---|
| 306 |
|
|---|
| 307 |
|
|---|
| 308 |
|
|---|
| 309 |
struct compat_ipt_replace { |
|---|
| 310 |
char name[IPT_TABLE_MAXNAMELEN]; |
|---|
| 311 |
u32 valid_hooks; |
|---|
| 312 |
u32 num_entries; |
|---|
| 313 |
u32 size; |
|---|
| 314 |
u32 hook_entry[NF_IP_NUMHOOKS]; |
|---|
| 315 |
u32 underflow[NF_IP_NUMHOOKS]; |
|---|
| 316 |
u32 num_counters; |
|---|
| 317 |
compat_uptr_t counters; |
|---|
| 318 |
struct ipt_entry entries[0]; |
|---|
| 319 |
}; |
|---|
| 320 |
|
|---|
| 321 |
static int do_netfilter_replace(int fd, int level, int optname, |
|---|
| 322 |
char __user *optval, int optlen) |
|---|
| 323 |
{ |
|---|
| 324 |
struct compat_ipt_replace __user *urepl; |
|---|
| 325 |
struct ipt_replace __user *repl_nat; |
|---|
| 326 |
char name[IPT_TABLE_MAXNAMELEN]; |
|---|
| 327 |
u32 origsize, tmp32, num_counters; |
|---|
| 328 |
unsigned int repl_nat_size; |
|---|
| 329 |
int ret; |
|---|
| 330 |
int i; |
|---|
| 331 |
compat_uptr_t ucntrs; |
|---|
| 332 |
|
|---|
| 333 |
urepl = (struct compat_ipt_replace __user *)optval; |
|---|
| 334 |
if (get_user(origsize, &urepl->size)) |
|---|
| 335 |
return -EFAULT; |
|---|
| 336 |
|
|---|
| 337 |
|
|---|
| 338 |
if (optlen != sizeof(*urepl) + origsize) |
|---|
| 339 |
return -ENOPROTOOPT; |
|---|
| 340 |
|
|---|
| 341 |
|
|---|
| 342 |
|
|---|
| 343 |
|
|---|
| 344 |
repl_nat_size = sizeof(*repl_nat) + origsize; |
|---|
| 345 |
repl_nat = compat_alloc_user_space(repl_nat_size); |
|---|
| 346 |
|
|---|
| 347 |
ret = -EFAULT; |
|---|
| 348 |
if (put_user(origsize, &repl_nat->size)) |
|---|
| 349 |
goto out; |
|---|
| 350 |
|
|---|
| 351 |
if (!access_ok(VERIFY_READ, urepl, optlen) || |
|---|
| 352 |
!access_ok(VERIFY_WRITE, repl_nat, optlen)) |
|---|
| 353 |
goto out; |
|---|
| 354 |
|
|---|
| 355 |
if (__copy_from_user(name, urepl->name, sizeof(urepl->name)) || |
|---|
| 356 |
__copy_to_user(repl_nat->name, name, sizeof(repl_nat->name))) |
|---|
| 357 |
goto out; |
|---|
| 358 |
|
|---|
| 359 |
if (__get_user(tmp32, &urepl->valid_hooks) || |
|---|
| 360 |
__put_user(tmp32, &repl_nat->valid_hooks)) |
|---|
| 361 |
goto out; |
|---|
| 362 |
|
|---|
| 363 |
if (__get_user(tmp32, &urepl->num_entries) || |
|---|
| 364 |
__put_user(tmp32, &repl_nat->num_entries)) |
|---|
| 365 |
goto out; |
|---|
| 366 |
|
|---|
| 367 |
if (__get_user(num_counters, &urepl->num_counters) || |
|---|
| 368 |
__put_user(num_counters, &repl_nat->num_counters)) |
|---|
| 369 |
goto out; |
|---|
| 370 |
|
|---|
| 371 |
if (__get_user(ucntrs, &urepl->counters) || |
|---|
| 372 |
__put_user(compat_ptr(ucntrs), &repl_nat->counters)) |
|---|
| 373 |
goto out; |
|---|
| 374 |
|
|---|
| 375 |
if (__copy_in_user(&repl_nat->entries[0], |
|---|
| 376 |
&urepl->entries[0], |
|---|
| 377 |
origsize)) |
|---|
| 378 |
goto out; |
|---|
| 379 |
|
|---|
| 380 |
for (i = 0; i < NF_IP_NUMHOOKS; i++) { |
|---|
| 381 |
if (__get_user(tmp32, &urepl->hook_entry[i]) || |
|---|
| 382 |
__put_user(tmp32, &repl_nat->hook_entry[i]) || |
|---|
| 383 |
__get_user(tmp32, &urepl->underflow[i]) || |
|---|
| 384 |
__put_user(tmp32, &repl_nat->underflow[i])) |
|---|
| 385 |
goto out; |
|---|
| 386 |
} |
|---|
| 387 |
|
|---|
| 388 |
|
|---|
| 389 |
|
|---|
| 390 |
|
|---|
| 391 |
|
|---|
| 392 |
|
|---|
| 393 |
|
|---|
| 394 |
if (!access_ok(VERIFY_WRITE, compat_ptr(ucntrs), |
|---|
| 395 |
num_counters * sizeof(struct ipt_counters))) |
|---|
| 396 |
goto out; |
|---|
| 397 |
|
|---|
| 398 |
|
|---|
| 399 |
ret = sys_setsockopt(fd, level, optname, |
|---|
| 400 |
(char __user *)repl_nat, repl_nat_size); |
|---|
| 401 |
|
|---|
| 402 |
out: |
|---|
| 403 |
return ret; |
|---|
| 404 |
} |
|---|
| 405 |
|
|---|
| 406 |
|
|---|
| 407 |
|
|---|
| 408 |
|
|---|
| 409 |
struct compat_sock_fprog { |
|---|
| 410 |
u16 len; |
|---|
| 411 |
compat_uptr_t filter; |
|---|
| 412 |
}; |
|---|
| 413 |
|
|---|
| 414 |
static int do_set_attach_filter(int fd, int level, int optname, |
|---|
| 415 |
char __user *optval, int optlen) |
|---|
| 416 |
{ |
|---|
| 417 |
struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; |
|---|
| 418 |
struct sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog)); |
|---|
| 419 |
compat_uptr_t ptr; |
|---|
| 420 |
u16 len; |
|---|
| 421 |
|
|---|
| 422 |
if (!access_ok(VERIFY_READ, fprog32, sizeof(*fprog32)) || |
|---|
| 423 |
!access_ok(VERIFY_WRITE, kfprog, sizeof(struct sock_fprog)) || |
|---|
| 424 |
__get_user(len, &fprog32->len) || |
|---|
| 425 |
__get_user(ptr, &fprog32->filter) || |
|---|
| 426 |
__put_user(len, &kfprog->len) || |
|---|
| 427 |
__put_user(compat_ptr(ptr), &kfprog->filter)) |
|---|
| 428 |
return -EFAULT; |
|---|
| 429 |
|
|---|
| 430 |
return sys_setsockopt(fd, level, optname, (char __user *)kfprog, |
|---|
| 431 |
sizeof(struct sock_fprog)); |
|---|
| 432 |
} |
|---|
| 433 |
|
|---|
| 434 |
static int do_set_sock_timeout(int fd, int level, int optname, char __user *optval, int optlen) |
|---|
| 435 |
{ |
|---|
| 436 |
struct compat_timeval __user *up = (struct compat_timeval __user *) optval; |
|---|
| 437 |
struct timeval ktime; |
|---|
| 438 |
mm_segment_t old_fs; |
|---|
| 439 |
int err; |
|---|
| 440 |
|
|---|
| 441 |
if (optlen < sizeof(*up)) |
|---|
| 442 |
return -EINVAL; |
|---|
| 443 |
if (!access_ok(VERIFY_READ, up, sizeof(*up)) || |
|---|
| 444 |
__get_user(ktime.tv_sec, &up->tv_sec) || |
|---|
| 445 |
__get_user(ktime.tv_usec, &up->tv_usec)) |
|---|
| 446 |
return -EFAULT; |
|---|
| 447 |
old_fs = get_fs(); |
|---|
| 448 |
set_fs(KERNEL_DS); |
|---|
| 449 |
err = sys_setsockopt(fd, level, optname, (char *) &ktime, sizeof(ktime)); |
|---|
| 450 |
set_fs(old_fs); |
|---|
| 451 |
|
|---|
| 452 |
return err; |
|---|
| 453 |
} |
|---|
| 454 |
|
|---|
| 455 |
asmlinkage long compat_sys_setsockopt(int fd, int level, int optname, |
|---|
| 456 |
char __user *optval, int optlen) |
|---|
| 457 |
{ |
|---|
| 458 |
if (optname == IPT_SO_SET_REPLACE) |
|---|
| 459 |
return do_netfilter_replace(fd, level, optname, |
|---|
| 460 |
optval, optlen); |
|---|
| 461 |
if (optname == SO_ATTACH_FILTER) |
|---|
| 462 |
return do_set_attach_filter(fd, level, optname, |
|---|
| 463 |
optval, optlen); |
|---|
| 464 |
if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) |
|---|
| 465 |
return do_set_sock_timeout(fd, level, optname, optval, optlen); |
|---|
| 466 |
|
|---|
| 467 |
return sys_setsockopt(fd, level, optname, optval, optlen); |
|---|
| 468 |
} |
|---|
| 469 |
|
|---|
| 470 |
static int do_get_sock_timeout(int fd, int level, int optname, |
|---|
| 471 |
char __user *optval, int __user *optlen) |
|---|
| 472 |
{ |
|---|
| 473 |
struct compat_timeval __user *up; |
|---|
| 474 |
struct timeval ktime; |
|---|
| 475 |
mm_segment_t old_fs; |
|---|
| 476 |
int len, err; |
|---|
| 477 |
|
|---|
| 478 |
up = (struct compat_timeval __user *) optval; |
|---|
| 479 |
if (get_user(len, optlen)) |
|---|
| 480 |
return -EFAULT; |
|---|
| 481 |
if (len < sizeof(*up)) |
|---|
| 482 |
return -EINVAL; |
|---|
| 483 |
len = sizeof(ktime); |
|---|
| 484 |
old_fs = get_fs(); |
|---|
| 485 |
set_fs(KERNEL_DS); |
|---|
| 486 |
err = sys_getsockopt(fd, level, optname, (char *) &ktime, &len); |
|---|
| 487 |
set_fs(old_fs); |
|---|
| 488 |
|
|---|
| 489 |
if (!err) { |
|---|
| 490 |
if (put_user(sizeof(*up), optlen) || |
|---|
| 491 |
!access_ok(VERIFY_WRITE, up, sizeof(*up)) || |
|---|
| 492 |
__put_user(ktime.tv_sec, &up->tv_sec) || |
|---|
| 493 |
__put_user(ktime.tv_usec, &up->tv_usec)) |
|---|
| 494 |
err = -EFAULT; |
|---|
| 495 |
} |
|---|
| 496 |
return err; |
|---|
| 497 |
} |
|---|
| 498 |
|
|---|
| 499 |
asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, |
|---|
| 500 |
char __user *optval, int __user *optlen) |
|---|
| 501 |
{ |
|---|
| 502 |
if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) |
|---|
| 503 |
return do_get_sock_timeout(fd, level, optname, optval, optlen); |
|---|
| 504 |
return sys_getsockopt(fd, level, optname, optval, optlen); |
|---|
| 505 |
} |
|---|
| 506 |
|
|---|
| 507 |
|
|---|
| 508 |
#define AL(x) ((x) * sizeof(u32)) |
|---|
| 509 |
static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), |
|---|
| 510 |
AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), |
|---|
| 511 |
AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; |
|---|
| 512 |
#undef AL |
|---|
| 513 |
|
|---|
| 514 |
asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) |
|---|
| 515 |
{ |
|---|
| 516 |
return sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); |
|---|
| 517 |
} |
|---|
| 518 |
|
|---|
| 519 |
asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, unsigned int flags) |
|---|
| 520 |
{ |
|---|
| 521 |
return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); |
|---|
| 522 |
} |
|---|
| 523 |
|
|---|
| 524 |
asmlinkage long compat_sys_socketcall(int call, u32 __user *args) |
|---|
| 525 |
{ |
|---|
| 526 |
int ret; |
|---|
| 527 |
u32 a[6]; |
|---|
| 528 |
u32 a0, a1; |
|---|
| 529 |
|
|---|
| 530 |
if (call < SYS_SOCKET || call > SYS_RECVMSG) |
|---|
| 531 |
return -EINVAL; |
|---|
| 532 |
if (copy_from_user(a, args, nas[call])) |
|---|
| 533 |
return -EFAULT; |
|---|
| 534 |
a0 = a[0]; |
|---|
| 535 |
a1 = a[1]; |
|---|
| 536 |
|
|---|
| 537 |
switch(call) { |
|---|
| 538 |
case SYS_SOCKET: |
|---|
| 539 |
ret = sys_socket(a0, a1, a[2]); |
|---|
| 540 |
break; |
|---|
| 541 |
case SYS_BIND: |
|---|
| 542 |
ret = sys_bind(a0, compat_ptr(a1), a[2]); |
|---|
| 543 |
break; |
|---|
| 544 |
case SYS_CONNECT: |
|---|
| 545 |
ret = sys_connect(a0, compat_ptr(a1), a[2]); |
|---|
| 546 |
break; |
|---|
| 547 |
case SYS_LISTEN: |
|---|
| 548 |
ret = sys_listen(a0, a1); |
|---|
| 549 |
break; |
|---|
| 550 |
case SYS_ACCEPT: |
|---|
| 551 |
ret = sys_accept(a0, compat_ptr(a1), compat_ptr(a[2])); |
|---|
| 552 |
break; |
|---|
| 553 |
case SYS_GETSOCKNAME: |
|---|
| 554 |
ret = sys_getsockname(a0, compat_ptr(a1), compat_ptr(a[2])); |
|---|
| 555 |
break; |
|---|
| 556 |
case SYS_GETPEERNAME: |
|---|
| 557 |
ret = sys_getpeername(a0, compat_ptr(a1), compat_ptr(a[2])); |
|---|
| 558 |
break; |
|---|
| 559 |
case SYS_SOCKETPAIR: |
|---|
| 560 |
ret = sys_socketpair(a0, a1, a[2], compat_ptr(a[3])); |
|---|
| 561 |
break; |
|---|
| 562 |
case SYS_SEND: |
|---|
| 563 |
ret = sys_send(a0, compat_ptr(a1), a[2], a[3]); |
|---|
| 564 |
break; |
|---|
| 565 |
case SYS_SENDTO: |
|---|
| 566 |
ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]); |
|---|
| 567 |
break; |
|---|
| 568 |
case SYS_RECV: |
|---|
| 569 |
ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]); |
|---|
| 570 |
break; |
|---|
| 571 |
case SYS_RECVFROM: |
|---|
| 572 |
ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5])); |
|---|
| 573 |
break; |
|---|
| 574 |
case SYS_SHUTDOWN: |
|---|
| 575 |
ret = sys_shutdown(a0,a1); |
|---|
| 576 |
break; |
|---|
| 577 |
case SYS_SETSOCKOPT: |
|---|
| 578 |
ret = compat_sys_setsockopt(a0, a1, a[2], |
|---|
| 579 |
compat_ptr(a[3]), a[4]); |
|---|
| 580 |
break; |
|---|
| 581 |
case SYS_GETSOCKOPT: |
|---|
| 582 |
ret = compat_sys_getsockopt(a0, a1, a[2], |
|---|
| 583 |
compat_ptr(a[3]), compat_ptr(a[4])); |
|---|
| 584 |
break; |
|---|
| 585 |
case SYS_SENDMSG: |
|---|
| 586 |
ret = compat_sys_sendmsg(a0, compat_ptr(a1), a[2]); |
|---|
| 587 |
break; |
|---|
| 588 |
case SYS_RECVMSG: |
|---|
| 589 |
ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); |
|---|
| 590 |
break; |
|---|
| 591 |
default: |
|---|
| 592 |
ret = -EINVAL; |
|---|
| 593 |
break; |
|---|
| 594 |
} |
|---|
| 595 |
return ret; |
|---|
| 596 |
} |
|---|