xref: /freebsd/sys/contrib/openzfs/module/os/linux/spl/spl-zone.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
1 /*
2  * Copyright (c) 2021 Klara Systems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/sysmacros.h>
29 #include <sys/kmem.h>
30 #include <linux/file.h>
31 #include <linux/magic.h>
32 #include <sys/zone.h>
33 
34 #if defined(CONFIG_USER_NS)
35 #include <linux/statfs.h>
36 #include <linux/proc_ns.h>
37 #endif
38 
39 #include <sys/mutex.h>
40 
41 static kmutex_t zone_datasets_lock;
42 static struct list_head zone_datasets;
43 
44 typedef struct zone_datasets {
45 	struct list_head zds_list;	/* zone_datasets linkage */
46 	struct user_namespace *zds_userns; /* namespace reference */
47 	struct list_head zds_datasets;	/* datasets for the namespace */
48 } zone_datasets_t;
49 
50 typedef struct zone_dataset {
51 	struct list_head zd_list;	/* zone_dataset linkage */
52 	size_t zd_dsnamelen;		/* length of name */
53 	char zd_dsname[];		/* name of the member dataset */
54 } zone_dataset_t;
55 
56 #if defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM)
57 /*
58  * Returns:
59  * - 0 on success
60  * - EBADF if it cannot open the provided file descriptor
61  * - ENOTTY if the file itself is a not a user namespace file. We want to
62  *   intercept this error in the ZFS layer. We cannot just return one of the
63  *   ZFS_ERR_* errors here as we want to preserve the seperation of the ZFS
64  *   and the SPL layers.
65  */
66 static int
67 user_ns_get(int fd, struct user_namespace **userns)
68 {
69 	struct kstatfs st;
70 	struct file *nsfile;
71 	struct ns_common *ns;
72 	int error;
73 
74 	if ((nsfile = fget(fd)) == NULL)
75 		return (EBADF);
76 	if (vfs_statfs(&nsfile->f_path, &st) != 0) {
77 		error = ENOTTY;
78 		goto done;
79 	}
80 	if (st.f_type != NSFS_MAGIC) {
81 		error = ENOTTY;
82 		goto done;
83 	}
84 	ns = get_proc_ns(file_inode(nsfile));
85 	if (ns->ops->type != CLONE_NEWUSER) {
86 		error = ENOTTY;
87 		goto done;
88 	}
89 	*userns = container_of(ns, struct user_namespace, ns);
90 
91 	error = 0;
92 done:
93 	fput(nsfile);
94 
95 	return (error);
96 }
97 #endif /* defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM) */
98 
99 static unsigned int
100 user_ns_zoneid(struct user_namespace *user_ns)
101 {
102 	unsigned int r;
103 
104 #if defined(HAVE_USER_NS_COMMON_INUM)
105 	r = user_ns->ns.inum;
106 #else
107 	r = user_ns->proc_inum;
108 #endif
109 
110 	return (r);
111 }
112 
113 static struct zone_datasets *
114 zone_datasets_lookup(unsigned int nsinum)
115 {
116 	zone_datasets_t *zds;
117 
118 	list_for_each_entry(zds, &zone_datasets, zds_list) {
119 		if (user_ns_zoneid(zds->zds_userns) == nsinum)
120 			return (zds);
121 	}
122 	return (NULL);
123 }
124 
125 #if defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM)
126 static struct zone_dataset *
127 zone_dataset_lookup(zone_datasets_t *zds, const char *dataset, size_t dsnamelen)
128 {
129 	zone_dataset_t *zd;
130 
131 	list_for_each_entry(zd, &zds->zds_datasets, zd_list) {
132 		if (zd->zd_dsnamelen != dsnamelen)
133 			continue;
134 		if (strncmp(zd->zd_dsname, dataset, dsnamelen) == 0)
135 			return (zd);
136 	}
137 
138 	return (NULL);
139 }
140 
141 static int
142 zone_dataset_cred_check(cred_t *cred)
143 {
144 
145 	if (!uid_eq(cred->uid, GLOBAL_ROOT_UID))
146 		return (EPERM);
147 
148 	return (0);
149 }
150 #endif /* defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM) */
151 
152 static int
153 zone_dataset_name_check(const char *dataset, size_t *dsnamelen)
154 {
155 
156 	if (dataset[0] == '\0' || dataset[0] == '/')
157 		return (ENOENT);
158 
159 	*dsnamelen = strlen(dataset);
160 	/* Ignore trailing slash, if supplied. */
161 	if (dataset[*dsnamelen - 1] == '/')
162 		(*dsnamelen)--;
163 
164 	return (0);
165 }
166 
167 int
168 zone_dataset_attach(cred_t *cred, const char *dataset, int userns_fd)
169 {
170 #if defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM)
171 	struct user_namespace *userns;
172 	zone_datasets_t *zds;
173 	zone_dataset_t *zd;
174 	int error;
175 	size_t dsnamelen;
176 
177 	if ((error = zone_dataset_cred_check(cred)) != 0)
178 		return (error);
179 	if ((error = zone_dataset_name_check(dataset, &dsnamelen)) != 0)
180 		return (error);
181 	if ((error = user_ns_get(userns_fd, &userns)) != 0)
182 		return (error);
183 
184 	mutex_enter(&zone_datasets_lock);
185 	zds = zone_datasets_lookup(user_ns_zoneid(userns));
186 	if (zds == NULL) {
187 		zds = kmem_alloc(sizeof (zone_datasets_t), KM_SLEEP);
188 		INIT_LIST_HEAD(&zds->zds_list);
189 		INIT_LIST_HEAD(&zds->zds_datasets);
190 		zds->zds_userns = userns;
191 		/*
192 		 * Lock the namespace by incresing its refcount to prevent
193 		 * the namespace ID from being reused.
194 		 */
195 		get_user_ns(userns);
196 		list_add_tail(&zds->zds_list, &zone_datasets);
197 	} else {
198 		zd = zone_dataset_lookup(zds, dataset, dsnamelen);
199 		if (zd != NULL) {
200 			mutex_exit(&zone_datasets_lock);
201 			return (EEXIST);
202 		}
203 	}
204 
205 	zd = kmem_alloc(sizeof (zone_dataset_t) + dsnamelen + 1, KM_SLEEP);
206 	zd->zd_dsnamelen = dsnamelen;
207 	strlcpy(zd->zd_dsname, dataset, dsnamelen + 1);
208 	INIT_LIST_HEAD(&zd->zd_list);
209 	list_add_tail(&zd->zd_list, &zds->zds_datasets);
210 
211 	mutex_exit(&zone_datasets_lock);
212 	return (0);
213 #else
214 	return (ENXIO);
215 #endif /* defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM) */
216 }
217 EXPORT_SYMBOL(zone_dataset_attach);
218 
219 int
220 zone_dataset_detach(cred_t *cred, const char *dataset, int userns_fd)
221 {
222 #if defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM)
223 	struct user_namespace *userns;
224 	zone_datasets_t *zds;
225 	zone_dataset_t *zd;
226 	int error;
227 	size_t dsnamelen;
228 
229 	if ((error = zone_dataset_cred_check(cred)) != 0)
230 		return (error);
231 	if ((error = zone_dataset_name_check(dataset, &dsnamelen)) != 0)
232 		return (error);
233 	if ((error = user_ns_get(userns_fd, &userns)) != 0)
234 		return (error);
235 
236 	mutex_enter(&zone_datasets_lock);
237 	zds = zone_datasets_lookup(user_ns_zoneid(userns));
238 	if (zds != NULL)
239 		zd = zone_dataset_lookup(zds, dataset, dsnamelen);
240 	if (zds == NULL || zd == NULL) {
241 		mutex_exit(&zone_datasets_lock);
242 		return (ENOENT);
243 	}
244 
245 	list_del(&zd->zd_list);
246 	kmem_free(zd, sizeof (*zd) + zd->zd_dsnamelen + 1);
247 
248 	/* Prune the namespace entry if it has no more delegations. */
249 	if (list_empty(&zds->zds_datasets)) {
250 		/*
251 		 * Decrease the refcount now that the namespace is no longer
252 		 * used. It is no longer necessary to prevent the namespace ID
253 		 * from being reused.
254 		 */
255 		put_user_ns(userns);
256 		list_del(&zds->zds_list);
257 		kmem_free(zds, sizeof (*zds));
258 	}
259 
260 	mutex_exit(&zone_datasets_lock);
261 	return (0);
262 #else
263 	return (ENXIO);
264 #endif /* defined(CONFIG_USER_NS) && defined(HAVE_USER_NS_COMMON_INUM) */
265 }
266 EXPORT_SYMBOL(zone_dataset_detach);
267 
268 /*
269  * A dataset is visible if:
270  * - It is a parent of a namespace entry.
271  * - It is one of the namespace entries.
272  * - It is a child of a namespace entry.
273  *
274  * A dataset is writable if:
275  * - It is one of the namespace entries.
276  * - It is a child of a namespace entry.
277  *
278  * The parent datasets of namespace entries are visible and
279  * read-only to provide a path back to the root of the pool.
280  */
281 int
282 zone_dataset_visible(const char *dataset, int *write)
283 {
284 	zone_datasets_t *zds;
285 	zone_dataset_t *zd;
286 	size_t dsnamelen, zd_len;
287 	int visible;
288 
289 	/* Default to read-only, in case visible is returned. */
290 	if (write != NULL)
291 		*write = 0;
292 	if (zone_dataset_name_check(dataset, &dsnamelen) != 0)
293 		return (0);
294 	if (INGLOBALZONE(curproc)) {
295 		if (write != NULL)
296 			*write = 1;
297 		return (1);
298 	}
299 
300 	mutex_enter(&zone_datasets_lock);
301 	zds = zone_datasets_lookup(crgetzoneid(curproc->cred));
302 	if (zds == NULL) {
303 		mutex_exit(&zone_datasets_lock);
304 		return (0);
305 	}
306 
307 	visible = 0;
308 	list_for_each_entry(zd, &zds->zds_datasets, zd_list) {
309 		zd_len = strlen(zd->zd_dsname);
310 		if (zd_len > dsnamelen) {
311 			/*
312 			 * The name of the namespace entry is longer than that
313 			 * of the dataset, so it could be that the dataset is a
314 			 * parent of the namespace entry.
315 			 */
316 			visible = memcmp(zd->zd_dsname, dataset,
317 			    dsnamelen) == 0 &&
318 			    zd->zd_dsname[dsnamelen] == '/';
319 			if (visible)
320 				break;
321 		} else if (zd_len == dsnamelen) {
322 			/*
323 			 * The name of the namespace entry is as long as that
324 			 * of the dataset, so perhaps the dataset itself is the
325 			 * namespace entry.
326 			 */
327 			visible = memcmp(zd->zd_dsname, dataset, zd_len) == 0;
328 			if (visible) {
329 				if (write != NULL)
330 					*write = 1;
331 				break;
332 			}
333 		} else {
334 			/*
335 			 * The name of the namespace entry is shorter than that
336 			 * of the dataset, so perhaps the dataset is a child of
337 			 * the namespace entry.
338 			 */
339 			visible = memcmp(zd->zd_dsname, dataset,
340 			    zd_len) == 0 && dataset[zd_len] == '/';
341 			if (visible) {
342 				if (write != NULL)
343 					*write = 1;
344 				break;
345 			}
346 		}
347 	}
348 
349 	mutex_exit(&zone_datasets_lock);
350 	return (visible);
351 }
352 EXPORT_SYMBOL(zone_dataset_visible);
353 
354 unsigned int
355 global_zoneid(void)
356 {
357 	unsigned int z = 0;
358 
359 #if defined(CONFIG_USER_NS)
360 	z = user_ns_zoneid(&init_user_ns);
361 #endif
362 
363 	return (z);
364 }
365 EXPORT_SYMBOL(global_zoneid);
366 
367 unsigned int
368 crgetzoneid(const cred_t *cr)
369 {
370 	unsigned int r = 0;
371 
372 #if defined(CONFIG_USER_NS)
373 	r = user_ns_zoneid(cr->user_ns);
374 #endif
375 
376 	return (r);
377 }
378 EXPORT_SYMBOL(crgetzoneid);
379 
380 boolean_t
381 inglobalzone(proc_t *proc)
382 {
383 #if defined(CONFIG_USER_NS)
384 	return (proc->cred->user_ns == &init_user_ns);
385 #else
386 	return (B_TRUE);
387 #endif
388 }
389 EXPORT_SYMBOL(inglobalzone);
390 
391 int
392 spl_zone_init(void)
393 {
394 	mutex_init(&zone_datasets_lock, NULL, MUTEX_DEFAULT, NULL);
395 	INIT_LIST_HEAD(&zone_datasets);
396 	return (0);
397 }
398 
399 void
400 spl_zone_fini(void)
401 {
402 	zone_datasets_t *zds;
403 	zone_dataset_t *zd;
404 
405 	/*
406 	 * It would be better to assert an empty zone_datasets, but since
407 	 * there's no automatic mechanism for cleaning them up if the user
408 	 * namespace is destroyed, just do it here, since spl is about to go
409 	 * out of context.
410 	 */
411 	while (!list_empty(&zone_datasets)) {
412 		zds = list_entry(zone_datasets.next, zone_datasets_t, zds_list);
413 		while (!list_empty(&zds->zds_datasets)) {
414 			zd = list_entry(zds->zds_datasets.next,
415 			    zone_dataset_t, zd_list);
416 			list_del(&zd->zd_list);
417 			kmem_free(zd, sizeof (*zd) + zd->zd_dsnamelen + 1);
418 		}
419 		put_user_ns(zds->zds_userns);
420 		list_del(&zds->zds_list);
421 		kmem_free(zds, sizeof (*zds));
422 	}
423 	mutex_destroy(&zone_datasets_lock);
424 }
425