diff options
Diffstat (limited to 'kernel/user_namespace.c')
-rw-r--r-- | kernel/user_namespace.c | 66 |
1 files changed, 52 insertions, 14 deletions
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 2b042c42fbc4..b14f4d342043 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/ctype.h> | 22 | #include <linux/ctype.h> |
23 | #include <linux/projid.h> | 23 | #include <linux/projid.h> |
24 | #include <linux/fs_struct.h> | ||
24 | 25 | ||
25 | static struct kmem_cache *user_ns_cachep __read_mostly; | 26 | static struct kmem_cache *user_ns_cachep __read_mostly; |
26 | 27 | ||
@@ -78,7 +79,7 @@ int create_user_ns(struct cred *new) | |||
78 | return ret; | 79 | return ret; |
79 | } | 80 | } |
80 | 81 | ||
81 | kref_init(&ns->kref); | 82 | atomic_set(&ns->count, 1); |
82 | /* Leave the new->user_ns reference with the new user namespace. */ | 83 | /* Leave the new->user_ns reference with the new user namespace. */ |
83 | ns->parent = parent_ns; | 84 | ns->parent = parent_ns; |
84 | ns->owner = owner; | 85 | ns->owner = owner; |
@@ -104,15 +105,16 @@ int unshare_userns(unsigned long unshare_flags, struct cred **new_cred) | |||
104 | return create_user_ns(cred); | 105 | return create_user_ns(cred); |
105 | } | 106 | } |
106 | 107 | ||
107 | void free_user_ns(struct kref *kref) | 108 | void free_user_ns(struct user_namespace *ns) |
108 | { | 109 | { |
109 | struct user_namespace *parent, *ns = | 110 | struct user_namespace *parent; |
110 | container_of(kref, struct user_namespace, kref); | ||
111 | 111 | ||
112 | parent = ns->parent; | 112 | do { |
113 | proc_free_inum(ns->proc_inum); | 113 | parent = ns->parent; |
114 | kmem_cache_free(user_ns_cachep, ns); | 114 | proc_free_inum(ns->proc_inum); |
115 | put_user_ns(parent); | 115 | kmem_cache_free(user_ns_cachep, ns); |
116 | ns = parent; | ||
117 | } while (atomic_dec_and_test(&parent->count)); | ||
116 | } | 118 | } |
117 | EXPORT_SYMBOL(free_user_ns); | 119 | EXPORT_SYMBOL(free_user_ns); |
118 | 120 | ||
@@ -519,6 +521,42 @@ struct seq_operations proc_projid_seq_operations = { | |||
519 | .show = projid_m_show, | 521 | .show = projid_m_show, |
520 | }; | 522 | }; |
521 | 523 | ||
524 | static bool mappings_overlap(struct uid_gid_map *new_map, struct uid_gid_extent *extent) | ||
525 | { | ||
526 | u32 upper_first, lower_first, upper_last, lower_last; | ||
527 | unsigned idx; | ||
528 | |||
529 | upper_first = extent->first; | ||
530 | lower_first = extent->lower_first; | ||
531 | upper_last = upper_first + extent->count - 1; | ||
532 | lower_last = lower_first + extent->count - 1; | ||
533 | |||
534 | for (idx = 0; idx < new_map->nr_extents; idx++) { | ||
535 | u32 prev_upper_first, prev_lower_first; | ||
536 | u32 prev_upper_last, prev_lower_last; | ||
537 | struct uid_gid_extent *prev; | ||
538 | |||
539 | prev = &new_map->extent[idx]; | ||
540 | |||
541 | prev_upper_first = prev->first; | ||
542 | prev_lower_first = prev->lower_first; | ||
543 | prev_upper_last = prev_upper_first + prev->count - 1; | ||
544 | prev_lower_last = prev_lower_first + prev->count - 1; | ||
545 | |||
546 | /* Does the upper range intersect a previous extent? */ | ||
547 | if ((prev_upper_first <= upper_last) && | ||
548 | (prev_upper_last >= upper_first)) | ||
549 | return true; | ||
550 | |||
551 | /* Does the lower range intersect a previous extent? */ | ||
552 | if ((prev_lower_first <= lower_last) && | ||
553 | (prev_lower_last >= lower_first)) | ||
554 | return true; | ||
555 | } | ||
556 | return false; | ||
557 | } | ||
558 | |||
559 | |||
522 | static DEFINE_MUTEX(id_map_mutex); | 560 | static DEFINE_MUTEX(id_map_mutex); |
523 | 561 | ||
524 | static ssize_t map_write(struct file *file, const char __user *buf, | 562 | static ssize_t map_write(struct file *file, const char __user *buf, |
@@ -531,7 +569,7 @@ static ssize_t map_write(struct file *file, const char __user *buf, | |||
531 | struct user_namespace *ns = seq->private; | 569 | struct user_namespace *ns = seq->private; |
532 | struct uid_gid_map new_map; | 570 | struct uid_gid_map new_map; |
533 | unsigned idx; | 571 | unsigned idx; |
534 | struct uid_gid_extent *extent, *last = NULL; | 572 | struct uid_gid_extent *extent = NULL; |
535 | unsigned long page = 0; | 573 | unsigned long page = 0; |
536 | char *kbuf, *pos, *next_line; | 574 | char *kbuf, *pos, *next_line; |
537 | ssize_t ret = -EINVAL; | 575 | ssize_t ret = -EINVAL; |
@@ -634,14 +672,11 @@ static ssize_t map_write(struct file *file, const char __user *buf, | |||
634 | if ((extent->lower_first + extent->count) <= extent->lower_first) | 672 | if ((extent->lower_first + extent->count) <= extent->lower_first) |
635 | goto out; | 673 | goto out; |
636 | 674 | ||
637 | /* For now only accept extents that are strictly in order */ | 675 | /* Do the ranges in extent overlap any previous extents? */ |
638 | if (last && | 676 | if (mappings_overlap(&new_map, extent)) |
639 | (((last->first + last->count) > extent->first) || | ||
640 | ((last->lower_first + last->count) > extent->lower_first))) | ||
641 | goto out; | 677 | goto out; |
642 | 678 | ||
643 | new_map.nr_extents++; | 679 | new_map.nr_extents++; |
644 | last = extent; | ||
645 | 680 | ||
646 | /* Fail if the file contains too many extents */ | 681 | /* Fail if the file contains too many extents */ |
647 | if ((new_map.nr_extents == UID_GID_MAP_MAX_EXTENTS) && | 682 | if ((new_map.nr_extents == UID_GID_MAP_MAX_EXTENTS) && |
@@ -803,6 +838,9 @@ static int userns_install(struct nsproxy *nsproxy, void *ns) | |||
803 | if (atomic_read(¤t->mm->mm_users) > 1) | 838 | if (atomic_read(¤t->mm->mm_users) > 1) |
804 | return -EINVAL; | 839 | return -EINVAL; |
805 | 840 | ||
841 | if (current->fs->users != 1) | ||
842 | return -EINVAL; | ||
843 | |||
806 | if (!ns_capable(user_ns, CAP_SYS_ADMIN)) | 844 | if (!ns_capable(user_ns, CAP_SYS_ADMIN)) |
807 | return -EPERM; | 845 | return -EPERM; |
808 | 846 | ||