1/*
2 * Copyright (c) 2015-2022 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
39static int nxop_ioctl(struct fileproc *, u_long, caddr_t, vfs_context_t);
40static int nxop_close(struct fileglob *, vfs_context_t);
41
42static const struct fileops nexus_ctl_ops = {
43 .fo_type = DTYPE_NEXUS,
44 .fo_read = fo_no_read,
45 .fo_write = fo_no_write,
46 .fo_ioctl = nxop_ioctl,
47 .fo_select = fo_no_select,
48 .fo_close = nxop_close,
49 .fo_drain = fo_no_drain,
50 .fo_kqfilter = fo_no_kqfilter,
51};
52
53static int
54nxop_ioctl(struct fileproc *fp, u_long cmd, caddr_t data, vfs_context_t ctx)
55{
56 struct nxctl *nxctl;
57 proc_t procp = vfs_context_proc(ctx);
58
59 if ((nxctl = (struct nxctl *)fp_get_data(fp)) == NULL) {
60 /* This is not a valid open file descriptor */
61 return EBADF;
62 }
63 return nxioctl(nxctl, cmd, data, procp);
64}
65
66static int
67nxop_close(struct fileglob *fg, vfs_context_t ctx)
68{
69#pragma unused(ctx)
70 struct nxctl *nxctl;
71 int error = 0;
72
73 nxctl = (struct nxctl *)fg_get_data(fg);
74 fg_set_data(fg, NULL);
75 if (nxctl != NULL) {
76 nxctl_dtor(nxctl);
77 }
78
79 return error;
80}
81
82int
83__nexus_open(struct proc *p, struct __nexus_open_args *uap, int *retval)
84{
85 struct nxctl *nxctl = NULL;
86 struct fileproc *fp = NULL;
87 struct nxctl_init init;
88 uuid_t nxctl_uuid;
89 int fd = -1, err = 0;
90 guardid_t guard;
91
92 if (__improbable(uap->init == USER_ADDR_NULL ||
93 uap->init_len < sizeof(init))) {
94 SK_DSC(p, "EINVAL: init %p, init_len %u", uap->init,
95 uap->init_len);
96 err = EINVAL;
97 goto done;
98 }
99
100 err = copyin(uap->init, (caddr_t)&init, sizeof(init));
101 if (__improbable(err != 0)) {
102 SK_DSC(p, "copyin err %d, init 0x%llx", err, SK_KVA(uap->init));
103 goto done;
104 }
105
106 if (__improbable(init.ni_version != NEXUSCTL_INIT_CURRENT_VERSION)) {
107 SK_DSC(p, "ENOTSUP: version %u != %u", init.ni_version,
108 NEXUSCTL_INIT_CURRENT_VERSION);
109 err = ENOTSUP;
110 goto done;
111 }
112
113 /* generate guard ID based on nexus controller UUID */
114 uuid_generate_random(out: nxctl_uuid);
115 sk_gen_guard_id(FALSE, nxctl_uuid, &guard);
116
117 err = falloc_guarded(p, fp: &fp, fd: &fd, ctx: vfs_context_current(), guard: &guard,
118 GUARD_CLOSE | GUARD_DUP | GUARD_SOCKET_IPC | GUARD_FILEPORT | GUARD_WRITE);
119 if (__improbable(err != 0)) {
120 SK_DSC(p, "falloc_guarded err %d", err);
121 goto done;
122 }
123
124 nxctl = nxctl_create(p, fp, nxctl_uuid, &err);
125 if (__improbable(nxctl == NULL)) {
126 ASSERT(err != 0);
127 SK_DSC(p, "nxctl_create err %d", err);
128 goto done;
129 }
130
131 /* update userland with respect to guard ID, etc. */
132 init.ni_guard = guard;
133 err = copyout(&init, uap->init, sizeof(init));
134 if (__improbable(err != 0)) {
135 SK_DSC(p, "copyout err %d, init 0x%llx", err,
136 SK_KVA(uap->init));
137 goto done;
138 }
139
140 fp->fp_flags |= FP_CLOEXEC | FP_CLOFORK;
141 fp->fp_glob->fg_flag |= (FREAD | FWRITE);
142 fp->fp_glob->fg_ops = &nexus_ctl_ops;
143 fp_set_data(fp, fg_data: nxctl); /* ref from nxctl_create */
144
145 proc_fdlock(p);
146 procfdtbl_releasefd(p, fd, NULL);
147 fp_drop(p, fd, fp, locked: 1);
148 proc_fdunlock(p);
149
150 *retval = fd;
151
152 SK_D("%s(%d) fd %d guard 0x%llx",
153 sk_proc_name_address(p), sk_proc_pid(p), fd, guard);
154
155done:
156 if (__improbable(err != 0)) {
157 if (nxctl != NULL) {
158 nxctl_dtor(nxctl);
159 nxctl = NULL;
160 }
161 if (fp != NULL) {
162 fp_free(p, fd, fp);
163 fp = NULL;
164 }
165 }
166
167 return err;
168}
169
170int
171__nexus_register(struct proc *p, struct __nexus_register_args *uap, int *retval)
172{
173#pragma unused(retval)
174 struct fileproc *fp;
175 struct kern_nexus_provider *nxprov = NULL;
176 struct nxctl *nxctl;
177 struct nxprov_reg reg;
178 int err = 0;
179
180 AUDIT_ARG(fd, uap->ctl);
181
182 if (__improbable(uap->reg == USER_ADDR_NULL ||
183 uap->reg_len < sizeof(reg) || uap->prov_uuid == USER_ADDR_NULL ||
184 uap->prov_uuid_len < sizeof(uuid_t))) {
185 SK_DSC(p, "EINVAL: reg 0x%llx, reg_len %u, prov_uuid 0x%llx, "
186 "prov_uuid_len %u", SK_KVA(uap->reg), uap->reg_len,
187 SK_KVA(uap->prov_uuid), uap->prov_uuid_len);
188 return EINVAL;
189 }
190
191 err = copyin(uap->reg, (caddr_t)&reg, sizeof(reg));
192 if (err != 0) {
193 SK_DSC(p, "copyin err %d, reg 0x%llx", err, SK_KVA(uap->reg));
194 return err;
195 }
196
197 if (__improbable(reg.nxpreg_version != NXPROV_REG_CURRENT_VERSION)) {
198 SK_DSC(p, "EINVAL: version %u != %u", reg.nxpreg_version,
199 NXPROV_REG_CURRENT_VERSION);
200 return EINVAL;
201 }
202
203 if (__improbable(reg.nxpreg_params.nxp_namelen == 0 ||
204 reg.nxpreg_params.nxp_namelen > sizeof(nexus_name_t))) {
205 SK_DSC(p, "EINVAL: namelen %u", reg.nxpreg_params.nxp_namelen);
206 return EINVAL;
207 }
208
209 err = fp_get_ftype(p, fd: uap->ctl, ftype: DTYPE_NEXUS, ENODEV, fpp: &fp);
210 if (__improbable(err != 0)) {
211 SK_DSC(p, "fp_get_ftype: %d", err);
212 return err;
213 }
214 nxctl = (struct nxctl *)fp_get_data(fp);
215
216 lck_mtx_lock(lck: &nxctl->nxctl_lock);
217 nxprov = nxprov_create(p, nxctl, &reg, &err);
218 lck_mtx_unlock(lck: &nxctl->nxctl_lock);
219 if (__improbable(nxprov == NULL)) {
220 ASSERT(err != 0);
221 SK_DSC(p, "nxprov_create: %d", err);
222 goto done;
223 }
224
225 err = copyout(&nxprov->nxprov_uuid, uap->prov_uuid, sizeof(uuid_t));
226 if (__improbable(err != 0)) {
227 SK_DSC(p, "copyout err %d, prov_uuid 0x%llx", err,
228 SK_KVA(uap->prov_uuid));
229 goto done;
230 }
231
232done:
233 fp_drop(p, fd: uap->ctl, fp, locked: 0);
234
235 if (__improbable(err != 0 && nxprov != NULL)) {
236 err = nxprov_close(nxprov, FALSE);
237 }
238
239 /* release extra ref from nxprov_create */
240 if (nxprov != NULL) {
241 nxprov_release(nxprov);
242 }
243
244 return err;
245}
246
247int
248__nexus_deregister(struct proc *p, struct __nexus_deregister_args *uap,
249 int *retval)
250{
251#pragma unused(retval)
252 struct fileproc *fp;
253 struct nxctl *nxctl = NULL;
254 uuid_t nxprov_uuid;
255 int err = 0;
256
257 AUDIT_ARG(fd, uap->ctl);
258
259 if (__improbable(uap->prov_uuid_len < sizeof(uuid_t))) {
260 SK_DSC(p, "EINVAL: prov_len %u < %u", uap->prov_uuid_len,
261 sizeof(uuid_t));
262 return EINVAL;
263 }
264
265 err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
266 if (__improbable(err != 0)) {
267 SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
268 SK_KVA(uap->prov_uuid));
269 return err;
270 }
271
272 if (__improbable(uuid_is_null(nxprov_uuid))) {
273 SK_DSC(p, "EINVAL: uuid_is_null");
274 return EINVAL;
275 }
276
277 err = fp_get_ftype(p, fd: uap->ctl, ftype: DTYPE_NEXUS, ENODEV, fpp: &fp);
278 if (__improbable(err != 0)) {
279 SK_DSC(p, "fp_get_ftype: %d", err);
280 return err;
281 }
282 nxctl = (struct nxctl *)fp_get_data(fp);
283
284 lck_mtx_lock(lck: &nxctl->nxctl_lock);
285 err = nxprov_destroy(nxctl, nxprov_uuid);
286 lck_mtx_unlock(lck: &nxctl->nxctl_lock);
287
288 fp_drop(p, fd: uap->ctl, fp, locked: 0);
289
290 return err;
291}
292
293int
294__nexus_create(struct proc *p, struct __nexus_create_args *uap, int *retval)
295{
296#pragma unused(retval)
297 struct fileproc *fp;
298 struct kern_nexus *nx = NULL;
299 struct nxctl *nxctl = NULL;
300 uuid_t nxprov_uuid;
301 int err = 0;
302
303 AUDIT_ARG(fd, uap->ctl);
304
305 if (__improbable(uap->prov_uuid_len < sizeof(uuid_t) ||
306 uap->nx_uuid_len < sizeof(uuid_t) ||
307 uap->nx_uuid == USER_ADDR_NULL)) {
308 SK_DSC(p, "EINVAL: prov_uuid_len %u, nx_uuid_len %u, "
309 "nx_uuid 0x%llx", uap->prov_uuid_len, uap->nx_uuid_len,
310 SK_KVA(uap->nx_uuid));
311 return EINVAL;
312 }
313
314 err = copyin(uap->prov_uuid, (caddr_t)&nxprov_uuid, sizeof(uuid_t));
315 if (__improbable(err != 0)) {
316 SK_DSC(p, "copyin err %d, prov_uuid 0x%llx", err,
317 SK_KVA(uap->prov_uuid));
318 return err;
319 }
320
321 if (__improbable(uuid_is_null(nxprov_uuid))) {
322 SK_DSC(p, "EINVAL: uuid_is_null");
323 return EINVAL;
324 }
325
326 err = fp_get_ftype(p, fd: uap->ctl, ftype: DTYPE_NEXUS, ENODEV, fpp: &fp);
327 if (__improbable(err != 0)) {
328 SK_DSC(p, "fp_get_ftype: %d", err);
329 return err;
330 }
331 nxctl = (struct nxctl *)fp_get_data(fp);
332
333 lck_mtx_lock(lck: &nxctl->nxctl_lock);
334 nx = nx_create(nxctl, nxprov_uuid, NEXUS_TYPE_UNDEFINED, NULL, NULL,
335 NULL, NULL, &err);
336 lck_mtx_unlock(lck: &nxctl->nxctl_lock);
337 if (__improbable(nx == NULL)) {
338 ASSERT(err != 0);
339 SK_DSC(p, "nx_create: %d", err);
340 goto done;
341 }
342 err = copyout(&nx->nx_uuid, uap->nx_uuid, sizeof(uuid_t));
343 if (__improbable(err != 0)) {
344 SK_DSC(p, "copyout err %d, nx_uuid 0x%llx", err,
345 SK_KVA(uap->nx_uuid));
346 goto done;
347 }
348
349done:
350 fp_drop(p, fd: uap->ctl, fp, locked: 0);
351
352 /* release extra ref from nx_create */
353 if (nx != NULL) {
354 (void) nx_release(nx);
355 }
356
357 return err;
358}
359
360int
361__nexus_destroy(struct proc *p, struct __nexus_destroy_args *uap, int *retval)
362{
363#pragma unused(retval)
364 struct fileproc *fp;
365 struct nxctl *nxctl = NULL;
366 int err = 0;
367 uuid_t nx_uuid;
368
369 AUDIT_ARG(fd, uap->ctl);
370
371 if (__improbable(uap->nx_uuid == USER_ADDR_NULL ||
372 uap->nx_uuid_len < sizeof(uuid_t))) {
373 SK_DSC(p, "EINVAL: nx_uuid 0x%llx, nx_uuid_len %u",
374 SK_KVA(uap->nx_uuid), uap->nx_uuid_len);
375 return EINVAL;
376 }
377
378 err = copyin(uap->nx_uuid, (caddr_t)&nx_uuid, sizeof(uuid_t));
379 if (__improbable(err != 0)) {
380 SK_DSC(p, "copyin err %d, nx_uuid 0x%llx", err,
381 SK_KVA(uap->nx_uuid));
382 return err;
383 }
384
385 if (__improbable(uuid_is_null(nx_uuid))) {
386 SK_DSC(p, "EINVAL: uuid_is_null");
387 return EINVAL;
388 }
389
390 err = fp_get_ftype(p, fd: uap->ctl, ftype: DTYPE_NEXUS, ENODEV, fpp: &fp);
391 if (__improbable(err != 0)) {
392 SK_DSC(p, "fp_get_ftype: %d", err);
393 return err;
394 }
395 nxctl = (struct nxctl *)fp_get_data(fp);
396
397 lck_mtx_lock(lck: &nxctl->nxctl_lock);
398 err = nx_destroy(nxctl, nx_uuid);
399 lck_mtx_unlock(lck: &nxctl->nxctl_lock);
400
401 fp_drop(p, fd: uap->ctl, fp, locked: 0);
402
403 return err;
404}
405
406int
407__nexus_get_opt(struct proc *p, struct __nexus_get_opt_args *uap, int *retval)
408{
409#pragma unused(retval)
410 struct fileproc *fp;
411 struct nxctl *nxctl = NULL;
412 struct sockopt sopt;
413 uint32_t optlen;
414 int err = 0;
415
416 AUDIT_ARG(fd, uap->ctl);
417
418 err = fp_get_ftype(p, fd: uap->ctl, ftype: DTYPE_NEXUS, ENODEV, fpp: &fp);
419 if (__improbable(err != 0)) {
420 SK_DSC(p, "fp_get_ftype: %d", err);
421 return err;
422 }
423 nxctl = (struct nxctl *)fp_get_data(fp);
424
425 if (__improbable(uap->aoptlen == USER_ADDR_NULL)) {
426 SK_DSC(p, "EINVAL: aoptlen == USER_ADDR_NULL");
427 err = EINVAL;
428 goto done;
429 }
430
431 if (uap->aoptval != USER_ADDR_NULL) {
432 err = copyin(uap->aoptlen, &optlen, sizeof(optlen));
433 if (__improbable(err != 0)) {
434 SK_DSC(p, "copyin err %d, aoptlen 0x%llx", err,
435 SK_KVA(uap->aoptlen));
436 goto done;
437 }
438 } else {
439 optlen = 0;
440 }
441
442 bzero(s: &sopt, n: sizeof(sopt));
443 sopt.sopt_dir = SOPT_GET;
444 sopt.sopt_name = uap->opt;
445 sopt.sopt_val = uap->aoptval;
446 sopt.sopt_valsize = optlen;
447 sopt.sopt_p = p;
448
449 lck_mtx_lock(lck: &nxctl->nxctl_lock);
450 err = nxctl_get_opt(nxctl, &sopt);
451 lck_mtx_unlock(lck: &nxctl->nxctl_lock);
452 if (__probable(err == 0)) {
453 optlen = (uint32_t)sopt.sopt_valsize;
454 err = copyout(&optlen, uap->aoptlen, sizeof(optlen));
455#if SK_LOG
456 if (__improbable(err != 0)) {
457 SK_DSC(p, "copyout err %d, aoptlen 0x%llx", err,
458 SK_KVA(uap->aoptlen));
459 }
460#endif /* SK_LOG */
461 }
462
463done:
464 fp_drop(p, fd: uap->ctl, fp, locked: 0);
465
466 return err;
467}
468
469int
470__nexus_set_opt(struct proc *p, struct __nexus_set_opt_args *uap, int *retval)
471{
472#pragma unused(retval)
473 struct fileproc *fp;
474 struct nxctl *nxctl = NULL;
475 struct sockopt sopt;
476 int err = 0;
477
478 bzero(s: &sopt, n: sizeof(sopt));
479 sopt.sopt_dir = SOPT_SET;
480 sopt.sopt_name = uap->opt;
481 sopt.sopt_val = uap->aoptval;
482 sopt.sopt_valsize = uap->optlen;
483 sopt.sopt_p = p;
484
485 if (uap->ctl != __OS_NEXUS_SHARED_USER_CONTROLLER_FD) {
486 AUDIT_ARG(fd, uap->ctl);
487
488 err = fp_get_ftype(p, fd: uap->ctl, ftype: DTYPE_NEXUS, ENODEV, fpp: &fp);
489 if (__improbable(err != 0)) {
490 SK_DSC(p, "fp_get_ftype: %d", err);
491 return err;
492 }
493 nxctl = (struct nxctl *)fp_get_data(fp);
494
495 lck_mtx_lock(lck: &nxctl->nxctl_lock);
496 err = nxctl_set_opt(nxctl, &sopt);
497 lck_mtx_unlock(lck: &nxctl->nxctl_lock);
498
499 fp_drop(p, fd: uap->ctl, fp, locked: 0);
500 } else {
501 /* opt that don't have nxctl uses shared user nxctl */
502 nxctl = usernxctl.ncd_nxctl;
503
504 lck_mtx_lock(lck: &nxctl->nxctl_lock);
505 err = nxctl_set_opt(nxctl, &sopt);
506 lck_mtx_unlock(lck: &nxctl->nxctl_lock);
507 }
508 return err;
509}
510