]>
Commit | Line | Data |
---|---|---|
dbec2846 EB |
1 | /* |
2 | * This program is free software; you can redistribute it and/or | |
3 | * modify it under the terms of the GNU General Public License as | |
4 | * published by the Free Software Foundation, version 2 of the | |
5 | * License. | |
6 | */ | |
7 | ||
8 | #include <linux/stat.h> | |
9 | #include <linux/sysctl.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/user_namespace.h> | |
12 | ||
13 | #ifdef CONFIG_SYSCTL | |
14 | static struct ctl_table_set * | |
15 | set_lookup(struct ctl_table_root *root) | |
16 | { | |
17 | return ¤t_user_ns()->set; | |
18 | } | |
19 | ||
20 | static int set_is_seen(struct ctl_table_set *set) | |
21 | { | |
22 | return ¤t_user_ns()->set == set; | |
23 | } | |
24 | ||
25 | static int set_permissions(struct ctl_table_header *head, | |
26 | struct ctl_table *table) | |
27 | { | |
28 | struct user_namespace *user_ns = | |
29 | container_of(head->set, struct user_namespace, set); | |
30 | int mode; | |
31 | ||
32 | /* Allow users with CAP_SYS_RESOURCE unrestrained access */ | |
33 | if (ns_capable(user_ns, CAP_SYS_RESOURCE)) | |
34 | mode = (table->mode & S_IRWXU) >> 6; | |
35 | else | |
36 | /* Allow all others at most read-only access */ | |
37 | mode = table->mode & S_IROTH; | |
38 | return (mode << 6) | (mode << 3) | mode; | |
39 | } | |
40 | ||
41 | static struct ctl_table_root set_root = { | |
42 | .lookup = set_lookup, | |
43 | .permissions = set_permissions, | |
44 | }; | |
45 | ||
b376c3e1 EB |
46 | static int zero = 0; |
47 | static int int_max = INT_MAX; | |
dbec2846 | 48 | static struct ctl_table userns_table[] = { |
b376c3e1 EB |
49 | { |
50 | .procname = "max_user_namespaces", | |
51 | .data = &init_user_ns.max_user_namespaces, | |
52 | .maxlen = sizeof(init_user_ns.max_user_namespaces), | |
53 | .mode = 0644, | |
54 | .proc_handler = proc_dointvec_minmax, | |
55 | .extra1 = &zero, | |
56 | .extra2 = &int_max, | |
57 | }, | |
dbec2846 EB |
58 | { } |
59 | }; | |
60 | #endif /* CONFIG_SYSCTL */ | |
61 | ||
62 | bool setup_userns_sysctls(struct user_namespace *ns) | |
63 | { | |
64 | #ifdef CONFIG_SYSCTL | |
65 | struct ctl_table *tbl; | |
66 | setup_sysctl_set(&ns->set, &set_root, set_is_seen); | |
67 | tbl = kmemdup(userns_table, sizeof(userns_table), GFP_KERNEL); | |
68 | if (tbl) { | |
b376c3e1 EB |
69 | tbl[0].data = &ns->max_user_namespaces; |
70 | ||
dbec2846 EB |
71 | ns->sysctls = __register_sysctl_table(&ns->set, "userns", tbl); |
72 | } | |
73 | if (!ns->sysctls) { | |
74 | kfree(tbl); | |
75 | retire_sysctl_set(&ns->set); | |
76 | return false; | |
77 | } | |
78 | #endif | |
79 | return true; | |
80 | } | |
81 | ||
82 | void retire_userns_sysctls(struct user_namespace *ns) | |
83 | { | |
84 | #ifdef CONFIG_SYSCTL | |
85 | struct ctl_table *tbl; | |
86 | ||
87 | tbl = ns->sysctls->ctl_table_arg; | |
88 | unregister_sysctl_table(ns->sysctls); | |
89 | retire_sysctl_set(&ns->set); | |
90 | kfree(tbl); | |
91 | #endif | |
92 | } | |
93 | ||
b376c3e1 EB |
94 | static inline bool atomic_inc_below(atomic_t *v, int u) |
95 | { | |
96 | int c, old; | |
97 | c = atomic_read(v); | |
98 | for (;;) { | |
99 | if (unlikely(c >= u)) | |
100 | return false; | |
101 | old = atomic_cmpxchg(v, c, c+1); | |
102 | if (likely(old == c)) | |
103 | return true; | |
104 | c = old; | |
105 | } | |
106 | } | |
107 | ||
108 | bool inc_user_namespaces(struct user_namespace *ns) | |
109 | { | |
110 | struct user_namespace *pos, *bad; | |
111 | for (pos = ns; pos; pos = pos->parent) { | |
112 | int max = READ_ONCE(pos->max_user_namespaces); | |
113 | if (!atomic_inc_below(&pos->user_namespaces, max)) | |
114 | goto fail; | |
115 | } | |
116 | return true; | |
117 | fail: | |
118 | bad = pos; | |
119 | for (pos = ns; pos != bad; pos = pos->parent) | |
120 | atomic_dec(&pos->user_namespaces); | |
121 | ||
122 | return false; | |
123 | } | |
124 | ||
125 | void dec_user_namespaces(struct user_namespace *ns) | |
126 | { | |
127 | struct user_namespace *pos; | |
128 | for (pos = ns; pos; pos = pos->parent) { | |
129 | int dec = atomic_dec_if_positive(&pos->user_namespaces); | |
130 | WARN_ON_ONCE(dec < 0); | |
131 | } | |
132 | } | |
133 | ||
dbec2846 EB |
134 | static __init int user_namespace_sysctl_init(void) |
135 | { | |
136 | #ifdef CONFIG_SYSCTL | |
137 | static struct ctl_table_header *userns_header; | |
138 | static struct ctl_table empty[1]; | |
139 | /* | |
140 | * It is necessary to register the userns directory in the | |
141 | * default set so that registrations in the child sets work | |
142 | * properly. | |
143 | */ | |
144 | userns_header = register_sysctl("userns", empty); | |
145 | BUG_ON(!userns_header); | |
146 | BUG_ON(!setup_userns_sysctls(&init_user_ns)); | |
147 | #endif | |
148 | return 0; | |
149 | } | |
150 | subsys_initcall(user_namespace_sysctl_init); | |
151 | ||
152 |