1 /*
2 * Copyright (c) 2015-2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/filedesc.h>
32 #include <sys/proc_internal.h>
33 #include <sys/file_internal.h>
34 #include <sys/vnode_internal.h>
35 #include <sys/sysproto.h>
36 #include <security/audit/audit.h>
37 #include <skywalk/os_skywalk_private.h>
38
39 static int nxop_close(struct fileglob *, vfs_context_t);
40
41 static const struct fileops nexus_ctl_ops = {
42 .fo_type = DTYPE_NEXUS,
43 .fo_read = fo_no_read,
44 .fo_write = fo_no_write,
45 .fo_ioctl = fo_no_ioctl,
46 .fo_select = fo_no_select,
47 .fo_close = nxop_close,
48 .fo_drain = fo_no_drain,
49 .fo_kqfilter = fo_no_kqfilter,
50 };
51
52 static int
nxop_close(struct fileglob * fg,vfs_context_t ctx)53 nxop_close(struct fileglob *fg, vfs_context_t ctx)
54 {
55 #pragma unused(ctx)
56 struct nxctl *nxctl;
57 int error = 0;
58
59 nxctl = (struct nxctl *)fg_get_data(fg);
60 fg_set_data(fg, NULL);
61 if (nxctl != NULL) {
62 nxctl_dtor(nxctl);
63 }
64
65 return error;
66 }
67
68 int
__nexus_open(struct proc * p,struct __nexus_open_args * uap,int * retval)69 __nexus_open(struct proc *p, struct __nexus_open_args *uap, int *retval)
70 {
71 struct nxctl *nxctl = NULL;
72 struct fileproc *fp = NULL;
73 struct nxctl_init init;
74 uuid_t nxctl_uuid;
75 int fd = -1, err = 0;
76 guardid_t guard;
77
78 if (__improbable(uap->init == USER_ADDR_NULL ||
79 uap->init_len < sizeof(init))) {
80 SK_DSC(p, "EINVAL: init %p, init_len %u", uap->init,
81 uap->init_len);
82 err = EINVAL;
83 goto done;
84 }
85
86 err = copyin(uap->init, (caddr_t)&init, sizeof(init));
87 if (__improbable(err != 0)) {
88 SK_DSC(p, "copyin err %d, init 0x%llx", err, SK_KVA(uap->init));
89 goto done;
90 }
91
92 if (__improbable(init.ni_version != NEXUSCTL_INIT_CURRENT_VERSION)) {
93 SK_DSC(p, "ENOTSUP: version %u != %u", init.ni_version,
94 NEXUSCTL_INIT_CURRENT_VERSION);
95 err = ENOTSUP;
96 goto done;
97 }
98
99 /* generate guard ID based on nexus controller UUID */
100 uuid_generate_random(nxctl_uuid);
101 sk_gen_guard_id(FALSE, nxctl_uuid, &guard);
102
103 err = falloc_guarded(p, &fp, &fd, vfs_context_current(), &guard,
104 GUARD_CLOSE | GUARD_DUP | GUARD_SOCKET_IPC | GUARD_FILEPORT | GUARD_WRITE);
105 if (__improbable(err != 0)) {
106 SK_DSC(p, "falloc_guarded err %d", err);
107 goto done;
108 }
109
110 nxctl = nxctl_create(p, fp, nxctl_uuid, &err);
111 if (__improbable(nxctl == NULL)) {
112 ASSERT(err != 0);
113 SK_DSC(p, "nxctl_create err %d", err);
114 goto done;
115 }
116
117 /* update userland with respect to guard ID, etc. */
118 init.ni_guard = guard;
119 err = copyout(&init, uap->init, sizeof(init));
120 if (__improbable(err != 0)) {
121 SK_DSC(p, "copyout err %d, init 0x%llx", err,
122 SK_KVA(uap->init));
123 goto done;
124 }
125
126 fp->fp_flags |= FP_CLOEXEC | FP_CLOFORK;
127 fp->fp_glob->fg_flag &= ~(FREAD | FWRITE);
128 fp->fp_glob->fg_ops = &nexus_ctl_ops;
129 fp_set_data(fp, nxctl); /* ref from nxctl_create */
130
131 proc_fdlock(p);
132 procfdtbl_releasefd(p, fd, NULL);
133 fp_drop(p, fd, fp, 1);
134 proc_fdunlock(p);
135
136 *retval = fd;
137
138 SK_D("%s(%d) fd %d guard 0x%llx",
139 sk_proc_name_address(p), sk_proc_pid(p), fd, guard);
140
141 done:
142 if (__improbable(err != 0)) {
143 if (nxctl != NULL) {
144 nxctl_dtor(nxctl);
145 nxctl = NULL;
146 }
147 if (fp != NULL) {
148 fp_free(p, fd, fp);
149 fp = NULL;
150 }
151 }
152
153 return err;
154 }
155
156 int
__nexus_register(struct proc * p,struct __nexus_register_args * uap,int * retval)157 __nexus_register(struct proc *p, struct __nexus_register_args *uap, int *retval)
158 {
159 #pragma unused(retval)
160 struct fileproc *fp;
161 struct kern_nexus_provider *nxprov = NULL;
162 struct nxctl *nxctl;
163 struct nxprov_reg reg;
164 int err = 0;
165
166 AUDIT_ARG(fd, uap->ctl);
167
168 if (__improbable(uap->reg == USER_ADDR_NULL ||
169 uap->reg_len < sizeof(reg) || uap->prov_uuid == USER_ADDR_NULL ||
170 uap->prov_uuid_len < sizeof(uuid_t))) {
171 SK_DSC(p, "EINVAL: reg 0x%llx, reg_len %u, prov_uuid 0x%llx, "
172 "prov_uuid_len %u", SK_KVA(uap->reg), uap->reg_len,
173 SK_KVA(uap->prov_uuid), uap->prov_uuid_len);
174 return EINVAL;
175 }
176
177 err = copyin(uap->reg, (caddr_t)®, sizeof(reg));
178 if (err != 0) {
179 SK_DSC(p, "copyin err %d, reg 0x%llx", err, SK_KVA(uap->reg));
180 return err;
181 }
182
183 if (__improbable(reg.nxpreg_version != NXPROV_REG_CURRENT_VERSION)) {
184 SK_DSC(p, "EINVAL: version %u != %u", reg.nxpreg_version,
185 NXPROV_REG_CURRENT_VERSION);
186 return EINVAL;
187 }
188
189 if (__improbable(reg.nxpreg_params.nxp_namelen == 0 ||
190 reg.nxpreg_params.nxp_namelen > sizeof(nexus_name_t))) {
191 SK_DSC(p, "EINVAL: namelen %u", reg.nxpreg_params.nxp_namelen);
192 return EINVAL;
193 }
194
195 err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
196 if (__improbable(err != 0)) {
197 SK_DSC(p, "fp_get_ftype: %d", err);
198 return err;
199 }
200 nxctl = (struct nxctl *)fp_get_data(fp);
201
202 lck_mtx_lock(&nxctl->nxctl_lock);
203 nxprov = nxprov_create(p, nxctl, ®, &err);
204 lck_mtx_unlock(&nxctl->nxctl_lock);
205 if (__improbable(nxprov == NULL)) {
206 ASSERT(err != 0);
207 SK_DSC(p, "nxprov_create: %d", err);
208 goto done;
209 }
210
211 err = copyout(&nxprov->nxprov_uuid, uap->prov_uuid, sizeof(uuid_t));
212 if (__improbable(err != 0)) {
213 SK_DSC(p, "copyout err %d, prov_uuid 0x%llx", err,
214 SK_KVA(uap->prov_uuid));
215 goto done;
216 }
217
218 done:
219 fp_drop(p, uap->ctl, fp, 0);
220
221 if (__improbable(err != 0 && nxprov != NULL)) {
222 err = nxprov_close(nxprov, FALSE);
223 }
224
225 /* release extra ref from nxprov_create */
226 if (nxprov != NULL) {
227 nxprov_release(nxprov);
228 }
229
230 return err;
231 }
232
233 int
__nexus_deregister(struct proc * p,struct __nexus_deregister_args * uap,int * retval)234 __nexus_deregister(struct proc *p, struct __nexus_deregister_args *uap,
235 int *retval)
236 {
237 #pragma unused(retval)
238 struct fileproc *fp;
239 struct nxctl *nxctl = NULL;
240 uuid_t nxprov_uuid;
241 int err = 0;
242
243 AUDIT_ARG(fd, uap->ctl);
244
245 if (__improbable(uap->prov_uuid_len < sizeof(uuid_t))) {
246 SK_DSC(p, "EINVAL: prov_len %u < %u", uap->prov_uuid_len,
247 sizeof(uuid_t));
248 return EINVAL;
249 }
250
251 err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
252 if (__improbable(err != 0)) {
253 SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
254 SK_KVA(uap->prov_uuid));
255 return err;
256 }
257
258 if (__improbable(uuid_is_null(nxprov_uuid))) {
259 SK_DSC(p, "EINVAL: uuid_is_null");
260 return EINVAL;
261 }
262
263 err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
264 if (__improbable(err != 0)) {
265 SK_DSC(p, "fp_get_ftype: %d", err);
266 return err;
267 }
268 nxctl = (struct nxctl *)fp_get_data(fp);
269
270 lck_mtx_lock(&nxctl->nxctl_lock);
271 err = nxprov_destroy(nxctl, nxprov_uuid);
272 lck_mtx_unlock(&nxctl->nxctl_lock);
273
274 fp_drop(p, uap->ctl, fp, 0);
275
276 return err;
277 }
278
279 int
__nexus_create(struct proc * p,struct __nexus_create_args * uap,int * retval)280 __nexus_create(struct proc *p, struct __nexus_create_args *uap, int *retval)
281 {
282 #pragma unused(retval)
283 struct fileproc *fp;
284 struct kern_nexus *nx = NULL;
285 struct nxctl *nxctl = NULL;
286 uuid_t nxprov_uuid;
287 int err = 0;
288
289 AUDIT_ARG(fd, uap->ctl);
290
291 if (__improbable(uap->prov_uuid_len < sizeof(uuid_t) ||
292 uap->nx_uuid_len < sizeof(uuid_t) ||
293 uap->nx_uuid == USER_ADDR_NULL)) {
294 SK_DSC(p, "EINVAL: prov_uuid_len %u, nx_uuid_len %u, "
295 "nx_uuid 0x%llx", uap->prov_uuid_len, uap->nx_uuid_len,
296 SK_KVA(uap->nx_uuid));
297 return EINVAL;
298 }
299
300 err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
301 if (__improbable(err != 0)) {
302 SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
303 SK_KVA(uap->prov_uuid));
304 return err;
305 }
306
307 if (__improbable(uuid_is_null(nxprov_uuid))) {
308 SK_DSC(p, "EINVAL: uuid_is_null");
309 return EINVAL;
310 }
311
312 err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
313 if (__improbable(err != 0)) {
314 SK_DSC(p, "fp_get_ftype: %d", err);
315 return err;
316 }
317 nxctl = (struct nxctl *)fp_get_data(fp);
318
319 lck_mtx_lock(&nxctl->nxctl_lock);
320 nx = nx_create(nxctl, nxprov_uuid, NEXUS_TYPE_UNDEFINED, NULL, NULL,
321 NULL, NULL, &err);
322 lck_mtx_unlock(&nxctl->nxctl_lock);
323 if (__improbable(nx == NULL)) {
324 ASSERT(err != 0);
325 SK_DSC(p, "nx_create: %d", err);
326 goto done;
327 }
328 err = copyout(&nx->nx_uuid, uap->nx_uuid, sizeof(uuid_t));
329 if (__improbable(err != 0)) {
330 SK_DSC(p, "copyout err %d, nx_uuid 0x%llx", err,
331 SK_KVA(uap->nx_uuid));
332 goto done;
333 }
334
335 done:
336 fp_drop(p, uap->ctl, fp, 0);
337
338 /* release extra ref from nx_create */
339 if (nx != NULL) {
340 (void) nx_release(nx);
341 }
342
343 return err;
344 }
345
346 int
__nexus_destroy(struct proc * p,struct __nexus_destroy_args * uap,int * retval)347 __nexus_destroy(struct proc *p, struct __nexus_destroy_args *uap, int *retval)
348 {
349 #pragma unused(retval)
350 struct fileproc *fp;
351 struct nxctl *nxctl = NULL;
352 int err = 0;
353 uuid_t nx_uuid;
354
355 AUDIT_ARG(fd, uap->ctl);
356
357 if (__improbable(uap->nx_uuid == USER_ADDR_NULL ||
358 uap->nx_uuid_len < sizeof(uuid_t))) {
359 SK_DSC(p, "EINVAL: nx_uuid 0x%llx, nx_uuid_len %u",
360 SK_KVA(uap->nx_uuid), uap->nx_uuid_len);
361 return EINVAL;
362 }
363
364 err = copyin(uap->nx_uuid, (caddr_t)&nx_uuid, sizeof(uuid_t));
365 if (__improbable(err != 0)) {
366 SK_DSC(p, "copyin err %d, nx_uuid 0x%llx", err,
367 SK_KVA(uap->nx_uuid));
368 return err;
369 }
370
371 if (__improbable(uuid_is_null(nx_uuid))) {
372 SK_DSC(p, "EINVAL: uuid_is_null");
373 return EINVAL;
374 }
375
376 err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
377 if (__improbable(err != 0)) {
378 SK_DSC(p, "fp_get_ftype: %d", err);
379 return err;
380 }
381 nxctl = (struct nxctl *)fp_get_data(fp);
382
383 lck_mtx_lock(&nxctl->nxctl_lock);
384 err = nx_destroy(nxctl, nx_uuid);
385 lck_mtx_unlock(&nxctl->nxctl_lock);
386
387 fp_drop(p, uap->ctl, fp, 0);
388
389 return err;
390 }
391
392 int
__nexus_get_opt(struct proc * p,struct __nexus_get_opt_args * uap,int * retval)393 __nexus_get_opt(struct proc *p, struct __nexus_get_opt_args *uap, int *retval)
394 {
395 #pragma unused(retval)
396 struct fileproc *fp;
397 struct nxctl *nxctl = NULL;
398 struct sockopt sopt;
399 uint32_t optlen;
400 int err = 0;
401
402 AUDIT_ARG(fd, uap->ctl);
403
404 err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
405 if (__improbable(err != 0)) {
406 SK_DSC(p, "fp_get_ftype: %d", err);
407 return err;
408 }
409 nxctl = (struct nxctl *)fp_get_data(fp);
410
411 if (__improbable(uap->aoptlen == USER_ADDR_NULL)) {
412 SK_DSC(p, "EINVAL: aoptlen == USER_ADDR_NULL");
413 err = EINVAL;
414 goto done;
415 }
416
417 if (uap->aoptval != USER_ADDR_NULL) {
418 err = copyin(uap->aoptlen, &optlen, sizeof(optlen));
419 if (__improbable(err != 0)) {
420 SK_DSC(p, "copyin err %d, aoptlen 0x%llx", err,
421 SK_KVA(uap->aoptlen));
422 goto done;
423 }
424 } else {
425 optlen = 0;
426 }
427
428 bzero(&sopt, sizeof(sopt));
429 sopt.sopt_dir = SOPT_GET;
430 sopt.sopt_name = uap->opt;
431 sopt.sopt_val = uap->aoptval;
432 sopt.sopt_valsize = optlen;
433 sopt.sopt_p = p;
434
435 lck_mtx_lock(&nxctl->nxctl_lock);
436 err = nxctl_get_opt(nxctl, &sopt);
437 lck_mtx_unlock(&nxctl->nxctl_lock);
438 if (__probable(err == 0)) {
439 optlen = (uint32_t)sopt.sopt_valsize;
440 err = copyout(&optlen, uap->aoptlen, sizeof(optlen));
441 #if SK_LOG
442 if (__improbable(err != 0)) {
443 SK_DSC(p, "copyout err %d, aoptlen 0x%llx", err,
444 SK_KVA(uap->aoptlen));
445 }
446 #endif /* SK_LOG */
447 }
448
449 done:
450 fp_drop(p, uap->ctl, fp, 0);
451
452 return err;
453 }
454
455 int
__nexus_set_opt(struct proc * p,struct __nexus_set_opt_args * uap,int * retval)456 __nexus_set_opt(struct proc *p, struct __nexus_set_opt_args *uap, int *retval)
457 {
458 #pragma unused(retval)
459 struct fileproc *fp;
460 struct nxctl *nxctl = NULL;
461 struct sockopt sopt;
462 int err = 0;
463
464 AUDIT_ARG(fd, uap->ctl);
465
466 err = fp_get_ftype(p, uap->ctl, DTYPE_NEXUS, ENODEV, &fp);
467 if (__improbable(err != 0)) {
468 SK_DSC(p, "fp_get_ftype: %d", err);
469 return err;
470 }
471 nxctl = (struct nxctl *)fp_get_data(fp);
472
473 bzero(&sopt, sizeof(sopt));
474 sopt.sopt_dir = SOPT_SET;
475 sopt.sopt_name = uap->opt;
476 sopt.sopt_val = uap->aoptval;
477 sopt.sopt_valsize = uap->optlen;
478 sopt.sopt_p = p;
479
480 lck_mtx_lock(&nxctl->nxctl_lock);
481 err = nxctl_set_opt(nxctl, &sopt);
482 lck_mtx_unlock(&nxctl->nxctl_lock);
483
484 fp_drop(p, uap->ctl, fp, 0);
485
486 return err;
487 }
488