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