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