diff options
| -rw-r--r-- | arch/alpha/kernel/ptrace.c | 2 | ||||
| -rw-r--r-- | arch/blackfin/kernel/ptrace.c | 4 | ||||
| -rw-r--r-- | arch/cris/arch-v32/kernel/ptrace.c | 2 | ||||
| -rw-r--r-- | arch/ia64/kernel/ptrace.c | 2 | ||||
| -rw-r--r-- | arch/mips/kernel/ptrace32.c | 4 | ||||
| -rw-r--r-- | arch/powerpc/kernel/ptrace32.c | 4 | ||||
| -rw-r--r-- | fs/exec.c | 21 | ||||
| -rw-r--r-- | include/linux/capability.h | 2 | ||||
| -rw-r--r-- | include/linux/mm.h | 2 | ||||
| -rw-r--r-- | include/linux/mm_types.h | 1 | ||||
| -rw-r--r-- | include/linux/ptrace.h | 4 | ||||
| -rw-r--r-- | include/linux/sched.h | 1 | ||||
| -rw-r--r-- | kernel/capability.c | 36 | ||||
| -rw-r--r-- | kernel/fork.c | 9 | ||||
| -rw-r--r-- | kernel/ptrace.c | 70 | ||||
| -rw-r--r-- | mm/init-mm.c | 2 | ||||
| -rw-r--r-- | mm/memory.c | 2 | ||||
| -rw-r--r-- | mm/nommu.c | 2 | ||||
| -rw-r--r-- | security/integrity/evm/evm_crypto.c | 12 |
19 files changed, 139 insertions, 43 deletions
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index 940dfb406591..04abdec7f496 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c | |||
| @@ -283,7 +283,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
| 283 | /* When I and D space are separate, these will need to be fixed. */ | 283 | /* When I and D space are separate, these will need to be fixed. */ |
| 284 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | 284 | case PTRACE_PEEKTEXT: /* read word at location addr. */ |
| 285 | case PTRACE_PEEKDATA: | 285 | case PTRACE_PEEKDATA: |
| 286 | copied = access_process_vm(child, addr, &tmp, sizeof(tmp), | 286 | copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp), |
| 287 | FOLL_FORCE); | 287 | FOLL_FORCE); |
| 288 | ret = -EIO; | 288 | ret = -EIO; |
| 289 | if (copied != sizeof(tmp)) | 289 | if (copied != sizeof(tmp)) |
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c index 8d79286ee4e8..360d99645163 100644 --- a/arch/blackfin/kernel/ptrace.c +++ b/arch/blackfin/kernel/ptrace.c | |||
| @@ -270,7 +270,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
| 270 | switch (bfin_mem_access_type(addr, to_copy)) { | 270 | switch (bfin_mem_access_type(addr, to_copy)) { |
| 271 | case BFIN_MEM_ACCESS_CORE: | 271 | case BFIN_MEM_ACCESS_CORE: |
| 272 | case BFIN_MEM_ACCESS_CORE_ONLY: | 272 | case BFIN_MEM_ACCESS_CORE_ONLY: |
| 273 | copied = access_process_vm(child, addr, &tmp, | 273 | copied = ptrace_access_vm(child, addr, &tmp, |
| 274 | to_copy, FOLL_FORCE); | 274 | to_copy, FOLL_FORCE); |
| 275 | if (copied) | 275 | if (copied) |
| 276 | break; | 276 | break; |
| @@ -323,7 +323,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
| 323 | switch (bfin_mem_access_type(addr, to_copy)) { | 323 | switch (bfin_mem_access_type(addr, to_copy)) { |
| 324 | case BFIN_MEM_ACCESS_CORE: | 324 | case BFIN_MEM_ACCESS_CORE: |
| 325 | case BFIN_MEM_ACCESS_CORE_ONLY: | 325 | case BFIN_MEM_ACCESS_CORE_ONLY: |
| 326 | copied = access_process_vm(child, addr, &data, | 326 | copied = ptrace_access_vm(child, addr, &data, |
| 327 | to_copy, | 327 | to_copy, |
| 328 | FOLL_FORCE | FOLL_WRITE); | 328 | FOLL_FORCE | FOLL_WRITE); |
| 329 | break; | 329 | break; |
diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c index f0df654ac6fc..fe1f9cf7b391 100644 --- a/arch/cris/arch-v32/kernel/ptrace.c +++ b/arch/cris/arch-v32/kernel/ptrace.c | |||
| @@ -147,7 +147,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
| 147 | /* The trampoline page is globally mapped, no page table to traverse.*/ | 147 | /* The trampoline page is globally mapped, no page table to traverse.*/ |
| 148 | tmp = *(unsigned long*)addr; | 148 | tmp = *(unsigned long*)addr; |
| 149 | } else { | 149 | } else { |
| 150 | copied = access_process_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE); | 150 | copied = ptrace_access_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE); |
| 151 | 151 | ||
| 152 | if (copied != sizeof(tmp)) | 152 | if (copied != sizeof(tmp)) |
| 153 | break; | 153 | break; |
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 31aa8c0f68e1..36f660da8124 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c | |||
| @@ -1159,7 +1159,7 @@ arch_ptrace (struct task_struct *child, long request, | |||
| 1159 | case PTRACE_PEEKTEXT: | 1159 | case PTRACE_PEEKTEXT: |
| 1160 | case PTRACE_PEEKDATA: | 1160 | case PTRACE_PEEKDATA: |
| 1161 | /* read word at location addr */ | 1161 | /* read word at location addr */ |
| 1162 | if (access_process_vm(child, addr, &data, sizeof(data), | 1162 | if (ptrace_access_vm(child, addr, &data, sizeof(data), |
| 1163 | FOLL_FORCE) | 1163 | FOLL_FORCE) |
| 1164 | != sizeof(data)) | 1164 | != sizeof(data)) |
| 1165 | return -EIO; | 1165 | return -EIO; |
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 7e71a4e0281b..5fcbdcd7abd0 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c | |||
| @@ -69,7 +69,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 69 | if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) | 69 | if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) |
| 70 | break; | 70 | break; |
| 71 | 71 | ||
| 72 | copied = access_process_vm(child, (u64)addrOthers, &tmp, | 72 | copied = ptrace_access_vm(child, (u64)addrOthers, &tmp, |
| 73 | sizeof(tmp), FOLL_FORCE); | 73 | sizeof(tmp), FOLL_FORCE); |
| 74 | if (copied != sizeof(tmp)) | 74 | if (copied != sizeof(tmp)) |
| 75 | break; | 75 | break; |
| @@ -178,7 +178,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 178 | if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) | 178 | if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) |
| 179 | break; | 179 | break; |
| 180 | ret = 0; | 180 | ret = 0; |
| 181 | if (access_process_vm(child, (u64)addrOthers, &data, | 181 | if (ptrace_access_vm(child, (u64)addrOthers, &data, |
| 182 | sizeof(data), | 182 | sizeof(data), |
| 183 | FOLL_FORCE | FOLL_WRITE) == sizeof(data)) | 183 | FOLL_FORCE | FOLL_WRITE) == sizeof(data)) |
| 184 | break; | 184 | break; |
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 010b7b310237..1e887f3a61a6 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c | |||
| @@ -73,7 +73,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 73 | if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) | 73 | if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) |
| 74 | break; | 74 | break; |
| 75 | 75 | ||
| 76 | copied = access_process_vm(child, (u64)addrOthers, &tmp, | 76 | copied = ptrace_access_vm(child, (u64)addrOthers, &tmp, |
| 77 | sizeof(tmp), FOLL_FORCE); | 77 | sizeof(tmp), FOLL_FORCE); |
| 78 | if (copied != sizeof(tmp)) | 78 | if (copied != sizeof(tmp)) |
| 79 | break; | 79 | break; |
| @@ -178,7 +178,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 178 | if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) | 178 | if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) |
| 179 | break; | 179 | break; |
| 180 | ret = 0; | 180 | ret = 0; |
| 181 | if (access_process_vm(child, (u64)addrOthers, &tmp, | 181 | if (ptrace_access_vm(child, (u64)addrOthers, &tmp, |
| 182 | sizeof(tmp), | 182 | sizeof(tmp), |
| 183 | FOLL_FORCE | FOLL_WRITE) == sizeof(tmp)) | 183 | FOLL_FORCE | FOLL_WRITE) == sizeof(tmp)) |
| 184 | break; | 184 | break; |
| @@ -1277,8 +1277,22 @@ EXPORT_SYMBOL(flush_old_exec); | |||
| 1277 | 1277 | ||
| 1278 | void would_dump(struct linux_binprm *bprm, struct file *file) | 1278 | void would_dump(struct linux_binprm *bprm, struct file *file) |
| 1279 | { | 1279 | { |
| 1280 | if (inode_permission(file_inode(file), MAY_READ) < 0) | 1280 | struct inode *inode = file_inode(file); |
| 1281 | if (inode_permission(inode, MAY_READ) < 0) { | ||
| 1282 | struct user_namespace *old, *user_ns; | ||
| 1281 | bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; | 1283 | bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; |
| 1284 | |||
| 1285 | /* Ensure mm->user_ns contains the executable */ | ||
| 1286 | user_ns = old = bprm->mm->user_ns; | ||
| 1287 | while ((user_ns != &init_user_ns) && | ||
| 1288 | !privileged_wrt_inode_uidgid(user_ns, inode)) | ||
| 1289 | user_ns = user_ns->parent; | ||
| 1290 | |||
| 1291 | if (old != user_ns) { | ||
| 1292 | bprm->mm->user_ns = get_user_ns(user_ns); | ||
| 1293 | put_user_ns(old); | ||
| 1294 | } | ||
| 1295 | } | ||
| 1282 | } | 1296 | } |
| 1283 | EXPORT_SYMBOL(would_dump); | 1297 | EXPORT_SYMBOL(would_dump); |
| 1284 | 1298 | ||
| @@ -1308,7 +1322,6 @@ void setup_new_exec(struct linux_binprm * bprm) | |||
| 1308 | !gid_eq(bprm->cred->gid, current_egid())) { | 1322 | !gid_eq(bprm->cred->gid, current_egid())) { |
| 1309 | current->pdeath_signal = 0; | 1323 | current->pdeath_signal = 0; |
| 1310 | } else { | 1324 | } else { |
| 1311 | would_dump(bprm, bprm->file); | ||
| 1312 | if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) | 1325 | if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) |
| 1313 | set_dumpable(current->mm, suid_dumpable); | 1326 | set_dumpable(current->mm, suid_dumpable); |
| 1314 | } | 1327 | } |
| @@ -1408,7 +1421,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm) | |||
| 1408 | unsigned n_fs; | 1421 | unsigned n_fs; |
| 1409 | 1422 | ||
| 1410 | if (p->ptrace) { | 1423 | if (p->ptrace) { |
| 1411 | if (p->ptrace & PT_PTRACE_CAP) | 1424 | if (ptracer_capable(p, current_user_ns())) |
| 1412 | bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP; | 1425 | bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP; |
| 1413 | else | 1426 | else |
| 1414 | bprm->unsafe |= LSM_UNSAFE_PTRACE; | 1427 | bprm->unsafe |= LSM_UNSAFE_PTRACE; |
| @@ -1743,6 +1756,8 @@ static int do_execveat_common(int fd, struct filename *filename, | |||
| 1743 | if (retval < 0) | 1756 | if (retval < 0) |
| 1744 | goto out; | 1757 | goto out; |
| 1745 | 1758 | ||
| 1759 | would_dump(bprm, bprm->file); | ||
| 1760 | |||
| 1746 | retval = exec_binprm(bprm); | 1761 | retval = exec_binprm(bprm); |
| 1747 | if (retval < 0) | 1762 | if (retval < 0) |
| 1748 | goto out; | 1763 | goto out; |
diff --git a/include/linux/capability.h b/include/linux/capability.h index dbc21c719ce6..6ffb67e10c06 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h | |||
| @@ -240,8 +240,10 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap) | |||
| 240 | return true; | 240 | return true; |
| 241 | } | 241 | } |
| 242 | #endif /* CONFIG_MULTIUSER */ | 242 | #endif /* CONFIG_MULTIUSER */ |
| 243 | extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode); | ||
| 243 | extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); | 244 | extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); |
| 244 | extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); | 245 | extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); |
| 246 | extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns); | ||
| 245 | 247 | ||
| 246 | /* audit system wants to get cap info from files as well */ | 248 | /* audit system wants to get cap info from files as well */ |
| 247 | extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); | 249 | extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); |
diff --git a/include/linux/mm.h b/include/linux/mm.h index a92c8d73aeaf..0b5b2e4df14e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
| @@ -1270,6 +1270,8 @@ extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void * | |||
| 1270 | unsigned int gup_flags); | 1270 | unsigned int gup_flags); |
| 1271 | extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, | 1271 | extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, |
| 1272 | void *buf, int len, unsigned int gup_flags); | 1272 | void *buf, int len, unsigned int gup_flags); |
| 1273 | extern int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, | ||
| 1274 | unsigned long addr, void *buf, int len, unsigned int gup_flags); | ||
| 1273 | 1275 | ||
| 1274 | long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm, | 1276 | long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm, |
| 1275 | unsigned long start, unsigned long nr_pages, | 1277 | unsigned long start, unsigned long nr_pages, |
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 4a8acedf4b7d..08d947fc4c59 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h | |||
| @@ -473,6 +473,7 @@ struct mm_struct { | |||
| 473 | */ | 473 | */ |
| 474 | struct task_struct __rcu *owner; | 474 | struct task_struct __rcu *owner; |
| 475 | #endif | 475 | #endif |
| 476 | struct user_namespace *user_ns; | ||
| 476 | 477 | ||
| 477 | /* store ref to file /proc/<pid>/exe symlink points to */ | 478 | /* store ref to file /proc/<pid>/exe symlink points to */ |
| 478 | struct file __rcu *exe_file; | 479 | struct file __rcu *exe_file; |
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 504c98a278d4..e0e539321ab9 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h | |||
| @@ -8,6 +8,9 @@ | |||
| 8 | #include <linux/pid_namespace.h> /* For task_active_pid_ns. */ | 8 | #include <linux/pid_namespace.h> /* For task_active_pid_ns. */ |
| 9 | #include <uapi/linux/ptrace.h> | 9 | #include <uapi/linux/ptrace.h> |
| 10 | 10 | ||
| 11 | extern int ptrace_access_vm(struct task_struct *tsk, unsigned long addr, | ||
| 12 | void *buf, int len, unsigned int gup_flags); | ||
| 13 | |||
| 11 | /* | 14 | /* |
| 12 | * Ptrace flags | 15 | * Ptrace flags |
| 13 | * | 16 | * |
| @@ -19,7 +22,6 @@ | |||
| 19 | #define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */ | 22 | #define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */ |
| 20 | #define PT_PTRACED 0x00000001 | 23 | #define PT_PTRACED 0x00000001 |
| 21 | #define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ | 24 | #define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */ |
| 22 | #define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */ | ||
| 23 | 25 | ||
| 24 | #define PT_OPT_FLAG_SHIFT 3 | 26 | #define PT_OPT_FLAG_SHIFT 3 |
| 25 | /* PT_TRACE_* event enable flags */ | 27 | /* PT_TRACE_* event enable flags */ |
diff --git a/include/linux/sched.h b/include/linux/sched.h index 5ccbbfe41345..a440cf178191 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
| @@ -1685,6 +1685,7 @@ struct task_struct { | |||
| 1685 | struct list_head cpu_timers[3]; | 1685 | struct list_head cpu_timers[3]; |
| 1686 | 1686 | ||
| 1687 | /* process credentials */ | 1687 | /* process credentials */ |
| 1688 | const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */ | ||
| 1688 | const struct cred __rcu *real_cred; /* objective and real subjective task | 1689 | const struct cred __rcu *real_cred; /* objective and real subjective task |
| 1689 | * credentials (COW) */ | 1690 | * credentials (COW) */ |
| 1690 | const struct cred __rcu *cred; /* effective (overridable) subjective task | 1691 | const struct cred __rcu *cred; /* effective (overridable) subjective task |
diff --git a/kernel/capability.c b/kernel/capability.c index 00411c82dac5..4984e1f552eb 100644 --- a/kernel/capability.c +++ b/kernel/capability.c | |||
| @@ -457,6 +457,19 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns, | |||
| 457 | EXPORT_SYMBOL(file_ns_capable); | 457 | EXPORT_SYMBOL(file_ns_capable); |
| 458 | 458 | ||
| 459 | /** | 459 | /** |
| 460 | * privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode? | ||
| 461 | * @ns: The user namespace in question | ||
| 462 | * @inode: The inode in question | ||
| 463 | * | ||
| 464 | * Return true if the inode uid and gid are within the namespace. | ||
| 465 | */ | ||
| 466 | bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode) | ||
| 467 | { | ||
| 468 | return kuid_has_mapping(ns, inode->i_uid) && | ||
| 469 | kgid_has_mapping(ns, inode->i_gid); | ||
| 470 | } | ||
| 471 | |||
| 472 | /** | ||
| 460 | * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped | 473 | * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped |
| 461 | * @inode: The inode in question | 474 | * @inode: The inode in question |
| 462 | * @cap: The capability in question | 475 | * @cap: The capability in question |
| @@ -469,7 +482,26 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap) | |||
| 469 | { | 482 | { |
| 470 | struct user_namespace *ns = current_user_ns(); | 483 | struct user_namespace *ns = current_user_ns(); |
| 471 | 484 | ||
| 472 | return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) && | 485 | return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode); |
| 473 | kgid_has_mapping(ns, inode->i_gid); | ||
| 474 | } | 486 | } |
| 475 | EXPORT_SYMBOL(capable_wrt_inode_uidgid); | 487 | EXPORT_SYMBOL(capable_wrt_inode_uidgid); |
| 488 | |||
| 489 | /** | ||
| 490 | * ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace | ||
| 491 | * @tsk: The task that may be ptraced | ||
| 492 | * @ns: The user namespace to search for CAP_SYS_PTRACE in | ||
| 493 | * | ||
| 494 | * Return true if the task that is ptracing the current task had CAP_SYS_PTRACE | ||
| 495 | * in the specified user namespace. | ||
| 496 | */ | ||
| 497 | bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns) | ||
| 498 | { | ||
| 499 | int ret = 0; /* An absent tracer adds no restrictions */ | ||
| 500 | const struct cred *cred; | ||
| 501 | rcu_read_lock(); | ||
| 502 | cred = rcu_dereference(tsk->ptracer_cred); | ||
| 503 | if (cred) | ||
| 504 | ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE); | ||
| 505 | rcu_read_unlock(); | ||
| 506 | return (ret == 0); | ||
| 507 | } | ||
diff --git a/kernel/fork.c b/kernel/fork.c index a439ac429669..869b8ccc00bf 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -747,7 +747,8 @@ static void mm_init_owner(struct mm_struct *mm, struct task_struct *p) | |||
| 747 | #endif | 747 | #endif |
| 748 | } | 748 | } |
| 749 | 749 | ||
| 750 | static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p) | 750 | static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, |
| 751 | struct user_namespace *user_ns) | ||
| 751 | { | 752 | { |
| 752 | mm->mmap = NULL; | 753 | mm->mmap = NULL; |
| 753 | mm->mm_rb = RB_ROOT; | 754 | mm->mm_rb = RB_ROOT; |
| @@ -787,6 +788,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p) | |||
| 787 | if (init_new_context(p, mm)) | 788 | if (init_new_context(p, mm)) |
| 788 | goto fail_nocontext; | 789 | goto fail_nocontext; |
| 789 | 790 | ||
| 791 | mm->user_ns = get_user_ns(user_ns); | ||
| 790 | return mm; | 792 | return mm; |
| 791 | 793 | ||
| 792 | fail_nocontext: | 794 | fail_nocontext: |
| @@ -832,7 +834,7 @@ struct mm_struct *mm_alloc(void) | |||
| 832 | return NULL; | 834 | return NULL; |
| 833 | 835 | ||
| 834 | memset(mm, 0, sizeof(*mm)); | 836 | memset(mm, 0, sizeof(*mm)); |
| 835 | return mm_init(mm, current); | 837 | return mm_init(mm, current, current_user_ns()); |
| 836 | } | 838 | } |
| 837 | 839 | ||
| 838 | /* | 840 | /* |
| @@ -847,6 +849,7 @@ void __mmdrop(struct mm_struct *mm) | |||
| 847 | destroy_context(mm); | 849 | destroy_context(mm); |
| 848 | mmu_notifier_mm_destroy(mm); | 850 | mmu_notifier_mm_destroy(mm); |
| 849 | check_mm(mm); | 851 | check_mm(mm); |
| 852 | put_user_ns(mm->user_ns); | ||
| 850 | free_mm(mm); | 853 | free_mm(mm); |
| 851 | } | 854 | } |
| 852 | EXPORT_SYMBOL_GPL(__mmdrop); | 855 | EXPORT_SYMBOL_GPL(__mmdrop); |
| @@ -1128,7 +1131,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk) | |||
| 1128 | 1131 | ||
| 1129 | memcpy(mm, oldmm, sizeof(*mm)); | 1132 | memcpy(mm, oldmm, sizeof(*mm)); |
| 1130 | 1133 | ||
| 1131 | if (!mm_init(mm, tsk)) | 1134 | if (!mm_init(mm, tsk, mm->user_ns)) |
| 1132 | goto fail_nomem; | 1135 | goto fail_nomem; |
| 1133 | 1136 | ||
| 1134 | err = dup_mmap(mm, oldmm); | 1137 | err = dup_mmap(mm, oldmm); |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index e6474f7272ec..49ba7c1ade9d 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
| @@ -27,6 +27,35 @@ | |||
| 27 | #include <linux/cn_proc.h> | 27 | #include <linux/cn_proc.h> |
| 28 | #include <linux/compat.h> | 28 | #include <linux/compat.h> |
| 29 | 29 | ||
| 30 | /* | ||
| 31 | * Access another process' address space via ptrace. | ||
| 32 | * Source/target buffer must be kernel space, | ||
| 33 | * Do not walk the page table directly, use get_user_pages | ||
| 34 | */ | ||
| 35 | int ptrace_access_vm(struct task_struct *tsk, unsigned long addr, | ||
| 36 | void *buf, int len, unsigned int gup_flags) | ||
| 37 | { | ||
| 38 | struct mm_struct *mm; | ||
| 39 | int ret; | ||
| 40 | |||
| 41 | mm = get_task_mm(tsk); | ||
| 42 | if (!mm) | ||
| 43 | return 0; | ||
| 44 | |||
| 45 | if (!tsk->ptrace || | ||
| 46 | (current != tsk->parent) || | ||
| 47 | ((get_dumpable(mm) != SUID_DUMP_USER) && | ||
| 48 | !ptracer_capable(tsk, mm->user_ns))) { | ||
| 49 | mmput(mm); | ||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags); | ||
| 54 | mmput(mm); | ||
| 55 | |||
| 56 | return ret; | ||
| 57 | } | ||
| 58 | |||
| 30 | 59 | ||
| 31 | /* | 60 | /* |
| 32 | * ptrace a task: make the debugger its new parent and | 61 | * ptrace a task: make the debugger its new parent and |
| @@ -39,6 +68,9 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | |||
| 39 | BUG_ON(!list_empty(&child->ptrace_entry)); | 68 | BUG_ON(!list_empty(&child->ptrace_entry)); |
| 40 | list_add(&child->ptrace_entry, &new_parent->ptraced); | 69 | list_add(&child->ptrace_entry, &new_parent->ptraced); |
| 41 | child->parent = new_parent; | 70 | child->parent = new_parent; |
| 71 | rcu_read_lock(); | ||
| 72 | child->ptracer_cred = get_cred(__task_cred(new_parent)); | ||
| 73 | rcu_read_unlock(); | ||
| 42 | } | 74 | } |
| 43 | 75 | ||
| 44 | /** | 76 | /** |
| @@ -71,12 +103,16 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | |||
| 71 | */ | 103 | */ |
| 72 | void __ptrace_unlink(struct task_struct *child) | 104 | void __ptrace_unlink(struct task_struct *child) |
| 73 | { | 105 | { |
| 106 | const struct cred *old_cred; | ||
| 74 | BUG_ON(!child->ptrace); | 107 | BUG_ON(!child->ptrace); |
| 75 | 108 | ||
| 76 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 109 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
| 77 | 110 | ||
| 78 | child->parent = child->real_parent; | 111 | child->parent = child->real_parent; |
| 79 | list_del_init(&child->ptrace_entry); | 112 | list_del_init(&child->ptrace_entry); |
| 113 | old_cred = child->ptracer_cred; | ||
| 114 | child->ptracer_cred = NULL; | ||
| 115 | put_cred(old_cred); | ||
| 80 | 116 | ||
| 81 | spin_lock(&child->sighand->siglock); | 117 | spin_lock(&child->sighand->siglock); |
| 82 | child->ptrace = 0; | 118 | child->ptrace = 0; |
| @@ -220,7 +256,7 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) | |||
| 220 | static int __ptrace_may_access(struct task_struct *task, unsigned int mode) | 256 | static int __ptrace_may_access(struct task_struct *task, unsigned int mode) |
| 221 | { | 257 | { |
| 222 | const struct cred *cred = current_cred(), *tcred; | 258 | const struct cred *cred = current_cred(), *tcred; |
| 223 | int dumpable = 0; | 259 | struct mm_struct *mm; |
| 224 | kuid_t caller_uid; | 260 | kuid_t caller_uid; |
| 225 | kgid_t caller_gid; | 261 | kgid_t caller_gid; |
| 226 | 262 | ||
| @@ -271,16 +307,11 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
| 271 | return -EPERM; | 307 | return -EPERM; |
| 272 | ok: | 308 | ok: |
| 273 | rcu_read_unlock(); | 309 | rcu_read_unlock(); |
| 274 | smp_rmb(); | 310 | mm = task->mm; |
| 275 | if (task->mm) | 311 | if (mm && |
| 276 | dumpable = get_dumpable(task->mm); | 312 | ((get_dumpable(mm) != SUID_DUMP_USER) && |
| 277 | rcu_read_lock(); | 313 | !ptrace_has_cap(mm->user_ns, mode))) |
| 278 | if (dumpable != SUID_DUMP_USER && | 314 | return -EPERM; |
| 279 | !ptrace_has_cap(__task_cred(task)->user_ns, mode)) { | ||
| 280 | rcu_read_unlock(); | ||
| 281 | return -EPERM; | ||
| 282 | } | ||
| 283 | rcu_read_unlock(); | ||
| 284 | 315 | ||
| 285 | return security_ptrace_access_check(task, mode); | 316 | return security_ptrace_access_check(task, mode); |
| 286 | } | 317 | } |
| @@ -344,10 +375,6 @@ static int ptrace_attach(struct task_struct *task, long request, | |||
| 344 | 375 | ||
| 345 | if (seize) | 376 | if (seize) |
| 346 | flags |= PT_SEIZED; | 377 | flags |= PT_SEIZED; |
| 347 | rcu_read_lock(); | ||
| 348 | if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE)) | ||
| 349 | flags |= PT_PTRACE_CAP; | ||
| 350 | rcu_read_unlock(); | ||
| 351 | task->ptrace = flags; | 378 | task->ptrace = flags; |
| 352 | 379 | ||
| 353 | __ptrace_link(task, current); | 380 | __ptrace_link(task, current); |
| @@ -537,7 +564,8 @@ int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst | |||
| 537 | int this_len, retval; | 564 | int this_len, retval; |
| 538 | 565 | ||
| 539 | this_len = (len > sizeof(buf)) ? sizeof(buf) : len; | 566 | this_len = (len > sizeof(buf)) ? sizeof(buf) : len; |
| 540 | retval = access_process_vm(tsk, src, buf, this_len, FOLL_FORCE); | 567 | retval = ptrace_access_vm(tsk, src, buf, this_len, FOLL_FORCE); |
| 568 | |||
| 541 | if (!retval) { | 569 | if (!retval) { |
| 542 | if (copied) | 570 | if (copied) |
| 543 | break; | 571 | break; |
| @@ -564,7 +592,7 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds | |||
| 564 | this_len = (len > sizeof(buf)) ? sizeof(buf) : len; | 592 | this_len = (len > sizeof(buf)) ? sizeof(buf) : len; |
| 565 | if (copy_from_user(buf, src, this_len)) | 593 | if (copy_from_user(buf, src, this_len)) |
| 566 | return -EFAULT; | 594 | return -EFAULT; |
| 567 | retval = access_process_vm(tsk, dst, buf, this_len, | 595 | retval = ptrace_access_vm(tsk, dst, buf, this_len, |
| 568 | FOLL_FORCE | FOLL_WRITE); | 596 | FOLL_FORCE | FOLL_WRITE); |
| 569 | if (!retval) { | 597 | if (!retval) { |
| 570 | if (copied) | 598 | if (copied) |
| @@ -1128,7 +1156,7 @@ int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr, | |||
| 1128 | unsigned long tmp; | 1156 | unsigned long tmp; |
| 1129 | int copied; | 1157 | int copied; |
| 1130 | 1158 | ||
| 1131 | copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE); | 1159 | copied = ptrace_access_vm(tsk, addr, &tmp, sizeof(tmp), FOLL_FORCE); |
| 1132 | if (copied != sizeof(tmp)) | 1160 | if (copied != sizeof(tmp)) |
| 1133 | return -EIO; | 1161 | return -EIO; |
| 1134 | return put_user(tmp, (unsigned long __user *)data); | 1162 | return put_user(tmp, (unsigned long __user *)data); |
| @@ -1139,7 +1167,7 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr, | |||
| 1139 | { | 1167 | { |
| 1140 | int copied; | 1168 | int copied; |
| 1141 | 1169 | ||
| 1142 | copied = access_process_vm(tsk, addr, &data, sizeof(data), | 1170 | copied = ptrace_access_vm(tsk, addr, &data, sizeof(data), |
| 1143 | FOLL_FORCE | FOLL_WRITE); | 1171 | FOLL_FORCE | FOLL_WRITE); |
| 1144 | return (copied == sizeof(data)) ? 0 : -EIO; | 1172 | return (copied == sizeof(data)) ? 0 : -EIO; |
| 1145 | } | 1173 | } |
| @@ -1157,7 +1185,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, | |||
| 1157 | switch (request) { | 1185 | switch (request) { |
| 1158 | case PTRACE_PEEKTEXT: | 1186 | case PTRACE_PEEKTEXT: |
| 1159 | case PTRACE_PEEKDATA: | 1187 | case PTRACE_PEEKDATA: |
| 1160 | ret = access_process_vm(child, addr, &word, sizeof(word), | 1188 | ret = ptrace_access_vm(child, addr, &word, sizeof(word), |
| 1161 | FOLL_FORCE); | 1189 | FOLL_FORCE); |
| 1162 | if (ret != sizeof(word)) | 1190 | if (ret != sizeof(word)) |
| 1163 | ret = -EIO; | 1191 | ret = -EIO; |
| @@ -1167,7 +1195,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, | |||
| 1167 | 1195 | ||
| 1168 | case PTRACE_POKETEXT: | 1196 | case PTRACE_POKETEXT: |
| 1169 | case PTRACE_POKEDATA: | 1197 | case PTRACE_POKEDATA: |
| 1170 | ret = access_process_vm(child, addr, &data, sizeof(data), | 1198 | ret = ptrace_access_vm(child, addr, &data, sizeof(data), |
| 1171 | FOLL_FORCE | FOLL_WRITE); | 1199 | FOLL_FORCE | FOLL_WRITE); |
| 1172 | ret = (ret != sizeof(data) ? -EIO : 0); | 1200 | ret = (ret != sizeof(data) ? -EIO : 0); |
| 1173 | break; | 1201 | break; |
diff --git a/mm/init-mm.c b/mm/init-mm.c index a56a851908d2..975e49f00f34 100644 --- a/mm/init-mm.c +++ b/mm/init-mm.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <linux/cpumask.h> | 6 | #include <linux/cpumask.h> |
| 7 | 7 | ||
| 8 | #include <linux/atomic.h> | 8 | #include <linux/atomic.h> |
| 9 | #include <linux/user_namespace.h> | ||
| 9 | #include <asm/pgtable.h> | 10 | #include <asm/pgtable.h> |
| 10 | #include <asm/mmu.h> | 11 | #include <asm/mmu.h> |
| 11 | 12 | ||
| @@ -21,5 +22,6 @@ struct mm_struct init_mm = { | |||
| 21 | .mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem), | 22 | .mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem), |
| 22 | .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock), | 23 | .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock), |
| 23 | .mmlist = LIST_HEAD_INIT(init_mm.mmlist), | 24 | .mmlist = LIST_HEAD_INIT(init_mm.mmlist), |
| 25 | .user_ns = &init_user_ns, | ||
| 24 | INIT_MM_CONTEXT(init_mm) | 26 | INIT_MM_CONTEXT(init_mm) |
| 25 | }; | 27 | }; |
diff --git a/mm/memory.c b/mm/memory.c index c264f7cd3e47..08d8da39de28 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
| @@ -3904,7 +3904,7 @@ EXPORT_SYMBOL_GPL(generic_access_phys); | |||
| 3904 | * Access another process' address space as given in mm. If non-NULL, use the | 3904 | * Access another process' address space as given in mm. If non-NULL, use the |
| 3905 | * given task for page fault accounting. | 3905 | * given task for page fault accounting. |
| 3906 | */ | 3906 | */ |
| 3907 | static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, | 3907 | int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, |
| 3908 | unsigned long addr, void *buf, int len, unsigned int gup_flags) | 3908 | unsigned long addr, void *buf, int len, unsigned int gup_flags) |
| 3909 | { | 3909 | { |
| 3910 | struct vm_area_struct *vma; | 3910 | struct vm_area_struct *vma; |
diff --git a/mm/nommu.c b/mm/nommu.c index 9720e0bab029..27bc543128e5 100644 --- a/mm/nommu.c +++ b/mm/nommu.c | |||
| @@ -1808,7 +1808,7 @@ void filemap_map_pages(struct fault_env *fe, | |||
| 1808 | } | 1808 | } |
| 1809 | EXPORT_SYMBOL(filemap_map_pages); | 1809 | EXPORT_SYMBOL(filemap_map_pages); |
| 1810 | 1810 | ||
| 1811 | static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, | 1811 | int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, |
| 1812 | unsigned long addr, void *buf, int len, unsigned int gup_flags) | 1812 | unsigned long addr, void *buf, int len, unsigned int gup_flags) |
| 1813 | { | 1813 | { |
| 1814 | struct vm_area_struct *vma; | 1814 | struct vm_area_struct *vma; |
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index bf663915412e..d7f282d75cc1 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c | |||
| @@ -151,8 +151,16 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, | |||
| 151 | memset(&hmac_misc, 0, sizeof(hmac_misc)); | 151 | memset(&hmac_misc, 0, sizeof(hmac_misc)); |
| 152 | hmac_misc.ino = inode->i_ino; | 152 | hmac_misc.ino = inode->i_ino; |
| 153 | hmac_misc.generation = inode->i_generation; | 153 | hmac_misc.generation = inode->i_generation; |
| 154 | hmac_misc.uid = from_kuid(inode->i_sb->s_user_ns, inode->i_uid); | 154 | /* The hmac uid and gid must be encoded in the initial user |
| 155 | hmac_misc.gid = from_kgid(inode->i_sb->s_user_ns, inode->i_gid); | 155 | * namespace (not the filesystems user namespace) as encoding |
| 156 | * them in the filesystems user namespace allows an attack | ||
| 157 | * where first they are written in an unprivileged fuse mount | ||
| 158 | * of a filesystem and then the system is tricked to mount the | ||
| 159 | * filesystem for real on next boot and trust it because | ||
| 160 | * everything is signed. | ||
| 161 | */ | ||
| 162 | hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid); | ||
| 163 | hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid); | ||
| 156 | hmac_misc.mode = inode->i_mode; | 164 | hmac_misc.mode = inode->i_mode; |
| 157 | crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); | 165 | crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); |
| 158 | if (evm_hmac_attrs & EVM_ATTR_FSUUID) | 166 | if (evm_hmac_attrs & EVM_ATTR_FSUUID) |
