1*eda14cbcSMatt Macy /*
2*eda14cbcSMatt Macy  * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3*eda14cbcSMatt Macy  * All rights reserved.
4*eda14cbcSMatt Macy  *
5*eda14cbcSMatt Macy  * Redistribution and use in source and binary forms, with or without
6*eda14cbcSMatt Macy  * modification, are permitted provided that the following conditions
7*eda14cbcSMatt Macy  * are met:
8*eda14cbcSMatt Macy  * 1. Redistributions of source code must retain the above copyright
9*eda14cbcSMatt Macy  *    notice, this list of conditions and the following disclaimer.
10*eda14cbcSMatt Macy  * 2. Redistributions in binary form must reproduce the above copyright
11*eda14cbcSMatt Macy  *    notice, this list of conditions and the following disclaimer in the
12*eda14cbcSMatt Macy  *    documentation and/or other materials provided with the distribution.
13*eda14cbcSMatt Macy  *
14*eda14cbcSMatt Macy  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15*eda14cbcSMatt Macy  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*eda14cbcSMatt Macy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*eda14cbcSMatt Macy  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18*eda14cbcSMatt Macy  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*eda14cbcSMatt Macy  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*eda14cbcSMatt Macy  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*eda14cbcSMatt Macy  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*eda14cbcSMatt Macy  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*eda14cbcSMatt Macy  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*eda14cbcSMatt Macy  * SUCH DAMAGE.
25*eda14cbcSMatt Macy  */
26*eda14cbcSMatt Macy 
27*eda14cbcSMatt Macy #include <sys/cdefs.h>
28*eda14cbcSMatt Macy __FBSDID("$FreeBSD$");
29*eda14cbcSMatt Macy 
30*eda14cbcSMatt Macy #include <sys/types.h>
31*eda14cbcSMatt Macy #include <sys/param.h>
32*eda14cbcSMatt Macy #include <sys/kernel.h>
33*eda14cbcSMatt Macy #include <sys/systm.h>
34*eda14cbcSMatt Macy #include <sys/proc.h>
35*eda14cbcSMatt Macy #include <sys/lock.h>
36*eda14cbcSMatt Macy #include <sys/mutex.h>
37*eda14cbcSMatt Macy #include <sys/sx.h>
38*eda14cbcSMatt Macy #include <sys/malloc.h>
39*eda14cbcSMatt Macy #include <sys/queue.h>
40*eda14cbcSMatt Macy #include <sys/jail.h>
41*eda14cbcSMatt Macy #include <sys/osd.h>
42*eda14cbcSMatt Macy #include <sys/priv.h>
43*eda14cbcSMatt Macy #include <sys/zone.h>
44*eda14cbcSMatt Macy 
45*eda14cbcSMatt Macy #include <sys/policy.h>
46*eda14cbcSMatt Macy 
47*eda14cbcSMatt Macy static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
48*eda14cbcSMatt Macy 
49*eda14cbcSMatt Macy /*
50*eda14cbcSMatt Macy  * Structure to record list of ZFS datasets exported to a zone.
51*eda14cbcSMatt Macy  */
52*eda14cbcSMatt Macy typedef struct zone_dataset {
53*eda14cbcSMatt Macy 	LIST_ENTRY(zone_dataset) zd_next;
54*eda14cbcSMatt Macy 	char	zd_dataset[0];
55*eda14cbcSMatt Macy } zone_dataset_t;
56*eda14cbcSMatt Macy 
57*eda14cbcSMatt Macy LIST_HEAD(zone_dataset_head, zone_dataset);
58*eda14cbcSMatt Macy 
59*eda14cbcSMatt Macy static int zone_slot;
60*eda14cbcSMatt Macy 
61*eda14cbcSMatt Macy int
62*eda14cbcSMatt Macy zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
63*eda14cbcSMatt Macy {
64*eda14cbcSMatt Macy 	struct zone_dataset_head *head;
65*eda14cbcSMatt Macy 	zone_dataset_t *zd, *zd2;
66*eda14cbcSMatt Macy 	struct prison *pr;
67*eda14cbcSMatt Macy 	int dofree, error;
68*eda14cbcSMatt Macy 
69*eda14cbcSMatt Macy 	if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
70*eda14cbcSMatt Macy 		return (error);
71*eda14cbcSMatt Macy 
72*eda14cbcSMatt Macy 	/* Allocate memory before we grab prison's mutex. */
73*eda14cbcSMatt Macy 	zd = malloc(sizeof (*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
74*eda14cbcSMatt Macy 
75*eda14cbcSMatt Macy 	sx_slock(&allprison_lock);
76*eda14cbcSMatt Macy 	pr = prison_find(jailid);	/* Locks &pr->pr_mtx. */
77*eda14cbcSMatt Macy 	sx_sunlock(&allprison_lock);
78*eda14cbcSMatt Macy 	if (pr == NULL) {
79*eda14cbcSMatt Macy 		free(zd, M_ZONES);
80*eda14cbcSMatt Macy 		return (ENOENT);
81*eda14cbcSMatt Macy 	}
82*eda14cbcSMatt Macy 
83*eda14cbcSMatt Macy 	head = osd_jail_get(pr, zone_slot);
84*eda14cbcSMatt Macy 	if (head != NULL) {
85*eda14cbcSMatt Macy 		dofree = 0;
86*eda14cbcSMatt Macy 		LIST_FOREACH(zd2, head, zd_next) {
87*eda14cbcSMatt Macy 			if (strcmp(dataset, zd2->zd_dataset) == 0) {
88*eda14cbcSMatt Macy 				free(zd, M_ZONES);
89*eda14cbcSMatt Macy 				error = EEXIST;
90*eda14cbcSMatt Macy 				goto end;
91*eda14cbcSMatt Macy 			}
92*eda14cbcSMatt Macy 		}
93*eda14cbcSMatt Macy 	} else {
94*eda14cbcSMatt Macy 		dofree = 1;
95*eda14cbcSMatt Macy 		prison_hold_locked(pr);
96*eda14cbcSMatt Macy 		mtx_unlock(&pr->pr_mtx);
97*eda14cbcSMatt Macy 		head = malloc(sizeof (*head), M_ZONES, M_WAITOK);
98*eda14cbcSMatt Macy 		LIST_INIT(head);
99*eda14cbcSMatt Macy 		mtx_lock(&pr->pr_mtx);
100*eda14cbcSMatt Macy 		error = osd_jail_set(pr, zone_slot, head);
101*eda14cbcSMatt Macy 		KASSERT(error == 0, ("osd_jail_set() failed (error=%d)",
102*eda14cbcSMatt Macy 		    error));
103*eda14cbcSMatt Macy 	}
104*eda14cbcSMatt Macy 	strcpy(zd->zd_dataset, dataset);
105*eda14cbcSMatt Macy 	LIST_INSERT_HEAD(head, zd, zd_next);
106*eda14cbcSMatt Macy end:
107*eda14cbcSMatt Macy 	if (dofree)
108*eda14cbcSMatt Macy 		prison_free_locked(pr);
109*eda14cbcSMatt Macy 	else
110*eda14cbcSMatt Macy 		mtx_unlock(&pr->pr_mtx);
111*eda14cbcSMatt Macy 	return (error);
112*eda14cbcSMatt Macy }
113*eda14cbcSMatt Macy 
114*eda14cbcSMatt Macy int
115*eda14cbcSMatt Macy zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
116*eda14cbcSMatt Macy {
117*eda14cbcSMatt Macy 	struct zone_dataset_head *head;
118*eda14cbcSMatt Macy 	zone_dataset_t *zd;
119*eda14cbcSMatt Macy 	struct prison *pr;
120*eda14cbcSMatt Macy 	int error;
121*eda14cbcSMatt Macy 
122*eda14cbcSMatt Macy 	if ((error = spl_priv_check_cred(cred, PRIV_ZFS_JAIL)) != 0)
123*eda14cbcSMatt Macy 		return (error);
124*eda14cbcSMatt Macy 
125*eda14cbcSMatt Macy 	sx_slock(&allprison_lock);
126*eda14cbcSMatt Macy 	pr = prison_find(jailid);
127*eda14cbcSMatt Macy 	sx_sunlock(&allprison_lock);
128*eda14cbcSMatt Macy 	if (pr == NULL)
129*eda14cbcSMatt Macy 		return (ENOENT);
130*eda14cbcSMatt Macy 	head = osd_jail_get(pr, zone_slot);
131*eda14cbcSMatt Macy 	if (head == NULL) {
132*eda14cbcSMatt Macy 		error = ENOENT;
133*eda14cbcSMatt Macy 		goto end;
134*eda14cbcSMatt Macy 	}
135*eda14cbcSMatt Macy 	LIST_FOREACH(zd, head, zd_next) {
136*eda14cbcSMatt Macy 		if (strcmp(dataset, zd->zd_dataset) == 0)
137*eda14cbcSMatt Macy 			break;
138*eda14cbcSMatt Macy 	}
139*eda14cbcSMatt Macy 	if (zd == NULL)
140*eda14cbcSMatt Macy 		error = ENOENT;
141*eda14cbcSMatt Macy 	else {
142*eda14cbcSMatt Macy 		LIST_REMOVE(zd, zd_next);
143*eda14cbcSMatt Macy 		free(zd, M_ZONES);
144*eda14cbcSMatt Macy 		if (LIST_EMPTY(head))
145*eda14cbcSMatt Macy 			osd_jail_del(pr, zone_slot);
146*eda14cbcSMatt Macy 		error = 0;
147*eda14cbcSMatt Macy 	}
148*eda14cbcSMatt Macy end:
149*eda14cbcSMatt Macy 	mtx_unlock(&pr->pr_mtx);
150*eda14cbcSMatt Macy 	return (error);
151*eda14cbcSMatt Macy }
152*eda14cbcSMatt Macy 
153*eda14cbcSMatt Macy /*
154*eda14cbcSMatt Macy  * Returns true if the named dataset is visible in the current zone.
155*eda14cbcSMatt Macy  * The 'write' parameter is set to 1 if the dataset is also writable.
156*eda14cbcSMatt Macy  */
157*eda14cbcSMatt Macy int
158*eda14cbcSMatt Macy zone_dataset_visible(const char *dataset, int *write)
159*eda14cbcSMatt Macy {
160*eda14cbcSMatt Macy 	struct zone_dataset_head *head;
161*eda14cbcSMatt Macy 	zone_dataset_t *zd;
162*eda14cbcSMatt Macy 	struct prison *pr;
163*eda14cbcSMatt Macy 	size_t len;
164*eda14cbcSMatt Macy 	int ret = 0;
165*eda14cbcSMatt Macy 
166*eda14cbcSMatt Macy 	if (dataset[0] == '\0')
167*eda14cbcSMatt Macy 		return (0);
168*eda14cbcSMatt Macy 	if (INGLOBALZONE(curproc)) {
169*eda14cbcSMatt Macy 		if (write != NULL)
170*eda14cbcSMatt Macy 			*write = 1;
171*eda14cbcSMatt Macy 		return (1);
172*eda14cbcSMatt Macy 	}
173*eda14cbcSMatt Macy 	pr = curthread->td_ucred->cr_prison;
174*eda14cbcSMatt Macy 	mtx_lock(&pr->pr_mtx);
175*eda14cbcSMatt Macy 	head = osd_jail_get(pr, zone_slot);
176*eda14cbcSMatt Macy 	if (head == NULL)
177*eda14cbcSMatt Macy 		goto end;
178*eda14cbcSMatt Macy 
179*eda14cbcSMatt Macy 	/*
180*eda14cbcSMatt Macy 	 * Walk the list once, looking for datasets which match exactly, or
181*eda14cbcSMatt Macy 	 * specify a dataset underneath an exported dataset.  If found, return
182*eda14cbcSMatt Macy 	 * true and note that it is writable.
183*eda14cbcSMatt Macy 	 */
184*eda14cbcSMatt Macy 	LIST_FOREACH(zd, head, zd_next) {
185*eda14cbcSMatt Macy 		len = strlen(zd->zd_dataset);
186*eda14cbcSMatt Macy 		if (strlen(dataset) >= len &&
187*eda14cbcSMatt Macy 		    bcmp(dataset, zd->zd_dataset, len) == 0 &&
188*eda14cbcSMatt Macy 		    (dataset[len] == '\0' || dataset[len] == '/' ||
189*eda14cbcSMatt Macy 		    dataset[len] == '@')) {
190*eda14cbcSMatt Macy 			if (write)
191*eda14cbcSMatt Macy 				*write = 1;
192*eda14cbcSMatt Macy 			ret = 1;
193*eda14cbcSMatt Macy 			goto end;
194*eda14cbcSMatt Macy 		}
195*eda14cbcSMatt Macy 	}
196*eda14cbcSMatt Macy 
197*eda14cbcSMatt Macy 	/*
198*eda14cbcSMatt Macy 	 * Walk the list a second time, searching for datasets which are parents
199*eda14cbcSMatt Macy 	 * of exported datasets.  These should be visible, but read-only.
200*eda14cbcSMatt Macy 	 *
201*eda14cbcSMatt Macy 	 * Note that we also have to support forms such as 'pool/dataset/', with
202*eda14cbcSMatt Macy 	 * a trailing slash.
203*eda14cbcSMatt Macy 	 */
204*eda14cbcSMatt Macy 	LIST_FOREACH(zd, head, zd_next) {
205*eda14cbcSMatt Macy 		len = strlen(dataset);
206*eda14cbcSMatt Macy 		if (dataset[len - 1] == '/')
207*eda14cbcSMatt Macy 			len--;	/* Ignore trailing slash */
208*eda14cbcSMatt Macy 		if (len < strlen(zd->zd_dataset) &&
209*eda14cbcSMatt Macy 		    bcmp(dataset, zd->zd_dataset, len) == 0 &&
210*eda14cbcSMatt Macy 		    zd->zd_dataset[len] == '/') {
211*eda14cbcSMatt Macy 			if (write)
212*eda14cbcSMatt Macy 				*write = 0;
213*eda14cbcSMatt Macy 			ret = 1;
214*eda14cbcSMatt Macy 			goto end;
215*eda14cbcSMatt Macy 		}
216*eda14cbcSMatt Macy 	}
217*eda14cbcSMatt Macy end:
218*eda14cbcSMatt Macy 	mtx_unlock(&pr->pr_mtx);
219*eda14cbcSMatt Macy 	return (ret);
220*eda14cbcSMatt Macy }
221*eda14cbcSMatt Macy 
222*eda14cbcSMatt Macy static void
223*eda14cbcSMatt Macy zone_destroy(void *arg)
224*eda14cbcSMatt Macy {
225*eda14cbcSMatt Macy 	struct zone_dataset_head *head;
226*eda14cbcSMatt Macy 	zone_dataset_t *zd;
227*eda14cbcSMatt Macy 
228*eda14cbcSMatt Macy 	head = arg;
229*eda14cbcSMatt Macy 	while ((zd = LIST_FIRST(head)) != NULL) {
230*eda14cbcSMatt Macy 		LIST_REMOVE(zd, zd_next);
231*eda14cbcSMatt Macy 		free(zd, M_ZONES);
232*eda14cbcSMatt Macy 	}
233*eda14cbcSMatt Macy 	free(head, M_ZONES);
234*eda14cbcSMatt Macy }
235*eda14cbcSMatt Macy 
236*eda14cbcSMatt Macy uint32_t
237*eda14cbcSMatt Macy zone_get_hostid(void *ptr)
238*eda14cbcSMatt Macy {
239*eda14cbcSMatt Macy 
240*eda14cbcSMatt Macy 	KASSERT(ptr == NULL, ("only NULL pointer supported in %s", __func__));
241*eda14cbcSMatt Macy 
242*eda14cbcSMatt Macy 	return ((uint32_t)curthread->td_ucred->cr_prison->pr_hostid);
243*eda14cbcSMatt Macy }
244*eda14cbcSMatt Macy 
245*eda14cbcSMatt Macy boolean_t
246*eda14cbcSMatt Macy in_globalzone(struct proc *p)
247*eda14cbcSMatt Macy {
248*eda14cbcSMatt Macy 	return (!jailed(FIRST_THREAD_IN_PROC((p))->td_ucred));
249*eda14cbcSMatt Macy }
250*eda14cbcSMatt Macy 
251*eda14cbcSMatt Macy static void
252*eda14cbcSMatt Macy zone_sysinit(void *arg __unused)
253*eda14cbcSMatt Macy {
254*eda14cbcSMatt Macy 
255*eda14cbcSMatt Macy 	zone_slot = osd_jail_register(zone_destroy, NULL);
256*eda14cbcSMatt Macy }
257*eda14cbcSMatt Macy 
258*eda14cbcSMatt Macy static void
259*eda14cbcSMatt Macy zone_sysuninit(void *arg __unused)
260*eda14cbcSMatt Macy {
261*eda14cbcSMatt Macy 
262*eda14cbcSMatt Macy 	osd_jail_deregister(zone_slot);
263*eda14cbcSMatt Macy }
264*eda14cbcSMatt Macy 
265*eda14cbcSMatt Macy SYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL);
266*eda14cbcSMatt Macy SYSUNINIT(zone_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysuninit, NULL);
267