]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_ktrace.c
399fb53b039c3e4db8bed0393b78d2bafe9e7f7e
[apple/xnu.git] / bsd / kern / kern_ktrace.c
1 /*
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
31 /*
32 * Copyright (c) 1989, 1993
33 * The Regents of the University of California. All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 *
63 * @(#)kern_ktrace.c 8.2 (Berkeley) 9/23/93
64 * $FreeBSD: src/sys/kern/kern_ktrace.c,v 1.35.2.4 2001/03/05 13:09:01 obrien Exp $
65 */
66
67
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/types.h>
71 #include <sys/proc_internal.h>
72 #include <sys/kauth.h>
73 #include <sys/file_internal.h>
74 #include <sys/namei.h>
75 #include <sys/vnode_internal.h>
76 #if KTRACE
77 #include <sys/ktrace.h>
78 #endif
79 #include <sys/malloc.h>
80 #include <sys/syslog.h>
81 #include <sys/sysproto.h>
82 #include <sys/uio_internal.h>
83
84 #include <bsm/audit_kernel.h>
85
86 #if KTRACE
87 static struct ktr_header *ktrgetheader(int type);
88 static void ktrwrite(struct vnode *, struct ktr_header *, struct uio *);
89 static int ktrcanset(struct proc *,struct proc *);
90 static int ktrsetchildren(struct proc *,struct proc *,
91 int, int, struct vnode *);
92 static int ktrops(struct proc *,struct proc *,int,int,struct vnode *);
93
94
95 static struct ktr_header *
96 ktrgetheader(type)
97 int type;
98 {
99 register struct ktr_header *kth;
100 struct proc *p = current_proc(); /* XXX */
101
102 MALLOC(kth, struct ktr_header *, sizeof (struct ktr_header),
103 M_KTRACE, M_WAITOK);
104 if (kth != NULL) {
105 kth->ktr_type = type;
106 microtime(&kth->ktr_time);
107 kth->ktr_pid = p->p_pid;
108 bcopy(p->p_comm, kth->ktr_comm, MAXCOMLEN);
109 }
110 return (kth);
111 }
112 #endif
113
114 void
115 ktrsyscall(p, code, narg, args)
116 struct proc *p;
117 int code, narg;
118 u_int64_t args[];
119 {
120 #if KTRACE
121 struct vnode *vp;
122 struct ktr_header *kth;
123 struct ktr_syscall *ktp;
124 register int len;
125 u_int64_t *argp;
126 int i;
127
128 if (!KTRPOINT(p, KTR_SYSCALL))
129 return;
130
131 vp = p->p_tracep;
132 len = __offsetof(struct ktr_syscall, ktr_args) +
133 (narg * sizeof(u_int64_t));
134 p->p_traceflag |= KTRFAC_ACTIVE;
135 kth = ktrgetheader(KTR_SYSCALL);
136 if (kth == NULL) {
137 p->p_traceflag &= ~KTRFAC_ACTIVE;
138 return;
139 }
140 MALLOC(ktp, struct ktr_syscall *, len, M_KTRACE, M_WAITOK);
141 if (ktp == NULL) {
142 FREE(kth, M_KTRACE);
143 return;
144 }
145 ktp->ktr_code = code;
146 ktp->ktr_narg = narg;
147 argp = &ktp->ktr_args[0];
148 for (i = 0; i < narg; i++)
149 *argp++ = args[i];
150 kth->ktr_buf = (caddr_t)ktp;
151 kth->ktr_len = len;
152 ktrwrite(vp, kth, NULL);
153 FREE(ktp, M_KTRACE);
154 FREE(kth, M_KTRACE);
155 p->p_traceflag &= ~KTRFAC_ACTIVE;
156 #else
157 return;
158 #endif
159 }
160
161 void
162 ktrsysret(p, code, error, retval)
163 struct proc *p;
164 int code, error;
165 register_t retval;
166 {
167 #if KTRACE
168 struct vnode *vp;
169 struct ktr_header *kth;
170 struct ktr_sysret ktp;
171
172 if (!KTRPOINT(p, KTR_SYSRET))
173 return;
174
175 vp = p->p_tracep;
176 p->p_traceflag |= KTRFAC_ACTIVE;
177 kth = ktrgetheader(KTR_SYSRET);
178 if (kth == NULL) {
179 p->p_traceflag &= ~KTRFAC_ACTIVE;
180 return;
181 }
182 ktp.ktr_code = code;
183 ktp.ktr_error = error;
184 ktp.ktr_retval = retval; /* what about val2 ? */
185
186 kth->ktr_buf = (caddr_t)&ktp;
187 kth->ktr_len = sizeof(struct ktr_sysret);
188
189 ktrwrite(vp, kth, NULL);
190 FREE(kth, M_KTRACE);
191 p->p_traceflag &= ~KTRFAC_ACTIVE;
192 #else
193 return;
194 #endif
195 }
196
197 #if KTRACE
198 void
199 ktrnamei(vp, path)
200 struct vnode *vp;
201 char *path;
202 {
203 struct ktr_header *kth;
204 struct proc *p = current_proc(); /* XXX */
205
206 p->p_traceflag |= KTRFAC_ACTIVE;
207 kth = ktrgetheader(KTR_NAMEI);
208 if (kth == NULL) {
209 p->p_traceflag &= ~KTRFAC_ACTIVE;
210 return;
211 }
212 kth->ktr_len = strlen(path);
213 kth->ktr_buf = path;
214
215 ktrwrite(vp, kth, NULL);
216 FREE(kth, M_KTRACE);
217 p->p_traceflag &= ~KTRFAC_ACTIVE;
218 }
219
220 void
221 ktrgenio(vp, fd, rw, uio, error)
222 struct vnode *vp;
223 int fd;
224 enum uio_rw rw;
225 struct uio *uio;
226 int error;
227 {
228 struct ktr_header *kth;
229 struct ktr_genio ktg;
230 struct proc *p = current_proc(); /* XXX */
231
232 if (error)
233 return;
234
235 p->p_traceflag |= KTRFAC_ACTIVE;
236 kth = ktrgetheader(KTR_GENIO);
237 if (kth == NULL) {
238 p->p_traceflag &= ~KTRFAC_ACTIVE;
239 return;
240 }
241 ktg.ktr_fd = fd;
242 ktg.ktr_rw = rw;
243 kth->ktr_buf = (caddr_t)&ktg;
244 kth->ktr_len = sizeof(struct ktr_genio);
245 uio->uio_offset = 0;
246 uio->uio_rw = UIO_WRITE;
247
248 ktrwrite(vp, kth, uio);
249 FREE(kth, M_KTRACE);
250 p->p_traceflag &= ~KTRFAC_ACTIVE;
251 }
252
253 void
254 ktrpsig(vp, sig, action, mask, code)
255 struct vnode *vp;
256 int sig;
257 sig_t action;
258 sigset_t *mask;
259 int code;
260 {
261 struct ktr_header *kth;
262 struct ktr_psig kp;
263 struct proc *p = current_proc(); /* XXX */
264
265 p->p_traceflag |= KTRFAC_ACTIVE;
266 kth = ktrgetheader(KTR_PSIG);
267 if (kth == NULL) {
268 p->p_traceflag &= ~KTRFAC_ACTIVE;
269 return;
270 }
271 kp.signo = (char)sig;
272 kp.action = action;
273 kp.mask = *mask;
274 kp.code = code;
275 kth->ktr_buf = (caddr_t)&kp;
276 kth->ktr_len = sizeof (struct ktr_psig);
277
278 ktrwrite(vp, kth, NULL);
279 FREE(kth, M_KTRACE);
280 p->p_traceflag &= ~KTRFAC_ACTIVE;
281 }
282
283 void
284 ktrcsw(vp, out, user)
285 struct vnode *vp;
286 int out, user;
287 {
288 struct ktr_header *kth;
289 struct ktr_csw kc;
290 struct proc *p = current_proc(); /* XXX */
291
292 p->p_traceflag |= KTRFAC_ACTIVE;
293 kth = ktrgetheader(KTR_CSW);
294 if (kth == NULL) {
295 p->p_traceflag &= ~KTRFAC_ACTIVE;
296 return;
297 }
298 kc.out = out;
299 kc.user = user;
300 kth->ktr_buf = (caddr_t)&kc;
301 kth->ktr_len = sizeof (struct ktr_csw);
302
303 ktrwrite(vp, kth, NULL);
304 FREE(kth, M_KTRACE);
305 p->p_traceflag &= ~KTRFAC_ACTIVE;
306 }
307 #endif /* KTRACE */
308
309 /* Interface and common routines */
310
311 /*
312 * ktrace system call
313 */
314 /* ARGSUSED */
315 int
316 ktrace(struct proc *curp, register struct ktrace_args *uap, __unused register_t *retval)
317 {
318 #if KTRACE
319 register struct vnode *vp = NULL;
320 register struct proc *p;
321 struct pgrp *pg;
322 int facs = uap->facs & ~KTRFAC_ROOT;
323 int ops = KTROP(uap->ops);
324 int descend = uap->ops & KTRFLAG_DESCEND;
325 int ret = 0;
326 int error = 0;
327 struct nameidata nd;
328 struct vfs_context context;
329
330 AUDIT_ARG(cmd, uap->ops);
331 AUDIT_ARG(pid, uap->pid);
332 AUDIT_ARG(value, uap->facs);
333
334 context.vc_proc = curp;
335 context.vc_ucred = kauth_cred_get();
336
337 curp->p_traceflag |= KTRFAC_ACTIVE;
338 if (ops != KTROP_CLEAR) {
339 /*
340 * an operation which requires a file argument.
341 */
342 NDINIT(&nd, LOOKUP, (NOFOLLOW|LOCKLEAF), UIO_USERSPACE,
343 uap->fname, &context);
344 error = vn_open(&nd, FREAD|FWRITE|O_NOFOLLOW, 0);
345 if (error) {
346 curp->p_traceflag &= ~KTRFAC_ACTIVE;
347 return (error);
348 }
349 vp = nd.ni_vp;
350
351 if (vp->v_type != VREG) {
352 (void) vn_close(vp, FREAD|FWRITE, kauth_cred_get(), curp);
353 (void) vnode_put(vp);
354
355 curp->p_traceflag &= ~KTRFAC_ACTIVE;
356 return (EACCES);
357 }
358 }
359 /*
360 * Clear all uses of the tracefile
361 */
362 if (ops == KTROP_CLEARFILE) {
363 LIST_FOREACH(p, &allproc, p_list) {
364 if (p->p_tracep == vp) {
365 if (ktrcanset(curp, p)) {
366 struct vnode *tvp = p->p_tracep;
367 /* no more tracing */
368 p->p_traceflag = 0;
369 if (tvp != NULL) {
370 p->p_tracep = NULL;
371 vnode_rele(tvp);
372 }
373 } else
374 error = EPERM;
375 }
376 }
377 goto done;
378 }
379
380 /*
381 * need something to (un)trace (XXX - why is this here?)
382 */
383 if (!facs) {
384 error = EINVAL;
385 goto done;
386 }
387 /*
388 * do it
389 */
390 if (uap->pid < 0) {
391 /*
392 * by process group
393 */
394 pg = pgfind(-uap->pid);
395 if (pg == NULL) {
396 error = ESRCH;
397 goto done;
398 }
399 LIST_FOREACH(p, &pg->pg_members, p_pglist)
400 if (descend)
401 ret |= ktrsetchildren(curp, p, ops, facs, vp);
402 else
403 ret |= ktrops(curp, p, ops, facs, vp);
404
405 } else {
406 /*
407 * by pid
408 */
409 p = pfind(uap->pid);
410 if (p == NULL) {
411 error = ESRCH;
412 goto done;
413 }
414 AUDIT_ARG(process, p);
415 if (descend)
416 ret |= ktrsetchildren(curp, p, ops, facs, vp);
417 else
418 ret |= ktrops(curp, p, ops, facs, vp);
419 }
420 if (!ret)
421 error = EPERM;
422 done:
423 if (vp != NULL) {
424 (void) vn_close(vp, FWRITE, kauth_cred_get(), curp);
425 (void) vnode_put(vp);
426 }
427 curp->p_traceflag &= ~KTRFAC_ACTIVE;
428 return (error);
429 #else
430 return ENOSYS;
431 #endif
432 }
433
434 /*
435 * utrace system call
436 */
437
438 /* ARGSUSED */
439 int
440 utrace(__unused struct proc *curp, register struct utrace_args *uap, __unused register_t *retval)
441 {
442 #if KTRACE
443 struct ktr_header *kth;
444 struct proc *p = current_proc(); /* XXX */
445 register caddr_t cp;
446
447 if (!KTRPOINT(p, KTR_USER))
448 return (0);
449 if (uap->len > KTR_USER_MAXLEN)
450 return (EINVAL);
451 p->p_traceflag |= KTRFAC_ACTIVE;
452 kth = ktrgetheader(KTR_USER);
453 if (kth == NULL) {
454 p->p_traceflag &= ~KTRFAC_ACTIVE;
455 return(ENOMEM);
456 }
457 MALLOC(cp, caddr_t, uap->len, M_KTRACE, M_WAITOK);
458 if (cp == NULL) {
459 FREE(kth, M_KTRACE);
460 return(ENOMEM);
461 }
462 if (copyin(uap->addr, cp, uap->len) == 0) {
463 kth->ktr_buf = cp;
464 kth->ktr_len = uap->len;
465 ktrwrite(p->p_tracep, kth, NULL);
466 }
467 FREE(kth, M_KTRACE);
468 FREE(cp, M_KTRACE);
469 p->p_traceflag &= ~KTRFAC_ACTIVE;
470
471 return (0);
472 #else
473 return (ENOSYS);
474 #endif
475 }
476
477 #if KTRACE
478 static int
479 ktrops(curp, p, ops, facs, vp)
480 struct proc *p, *curp;
481 int ops, facs;
482 struct vnode *vp;
483 {
484 struct vnode *tvp;
485
486 if (!ktrcanset(curp, p))
487 return (0);
488 if (ops == KTROP_SET) {
489 if (p->p_tracep != vp) {
490 tvp = p->p_tracep;
491 vnode_ref(vp);
492 p->p_tracep = vp;
493
494 if (tvp != NULL) {
495 /*
496 * if trace file already in use, relinquish
497 */
498 vnode_rele(tvp);
499 }
500 }
501 p->p_traceflag |= facs;
502 if (!suser(kauth_cred_get(), NULL))
503 p->p_traceflag |= KTRFAC_ROOT;
504 } else {
505 /* KTROP_CLEAR */
506 if (((p->p_traceflag &= ~facs) & KTRFAC_MASK) == 0) {
507 /* no more tracing */
508 tvp = p->p_tracep;
509 p->p_traceflag = 0;
510 if (tvp != NULL) {
511 p->p_tracep = NULL;
512 vnode_rele(tvp);
513 }
514 }
515 }
516
517 return (1);
518 }
519
520 static int
521 ktrsetchildren(curp, top, ops, facs, vp)
522 struct proc *curp, *top;
523 int ops, facs;
524 struct vnode *vp;
525 {
526 register struct proc *p;
527 register int ret = 0;
528
529 p = top;
530 for (;;) {
531 ret |= ktrops(curp, p, ops, facs, vp);
532 /*
533 * If this process has children, descend to them next,
534 * otherwise do any siblings, and if done with this level,
535 * follow back up the tree (but not past top).
536 */
537 if (!LIST_EMPTY(&p->p_children))
538 p = LIST_FIRST(&p->p_children);
539 else for (;;) {
540 if (p == top)
541 return (ret);
542 if (LIST_NEXT(p, p_sibling)) {
543 p = LIST_NEXT(p, p_sibling);
544 break;
545 }
546 p = p->p_pptr;
547 }
548 }
549 /*NOTREACHED*/
550 }
551
552 static void
553 ktrwrite(struct vnode *vp, struct ktr_header *kth, struct uio *uio)
554 {
555 uio_t auio;
556 register struct proc *p = current_proc(); /* XXX */
557 struct vfs_context context;
558 int error;
559 char uio_buf[ UIO_SIZEOF(2) ];
560
561 if (vp == NULL)
562 return;
563
564 auio = uio_createwithbuffer(2, 0, UIO_SYSSPACE, UIO_WRITE,
565 &uio_buf[0], sizeof(uio_buf));
566 uio_addiov(auio, CAST_USER_ADDR_T(kth), sizeof(struct ktr_header));
567 context.vc_proc = p;
568 context.vc_ucred = kauth_cred_get();
569
570 if (kth->ktr_len > 0) {
571 uio_addiov(auio, CAST_USER_ADDR_T(kth->ktr_buf), kth->ktr_len);
572 if (uio != NULL)
573 kth->ktr_len += uio_resid(uio);
574 }
575 if ((error = vnode_getwithref(vp)) == 0) {
576 error = VNOP_WRITE(vp, auio, IO_UNIT | IO_APPEND, &context);
577 if (error == 0 && uio != NULL) {
578 error = VNOP_WRITE(vp, uio, IO_UNIT | IO_APPEND, &context);
579 }
580 vnode_put(vp);
581 }
582 if (error) {
583 /*
584 * If error encountered, give up tracing on this vnode.
585 */
586 log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n",
587 error);
588 LIST_FOREACH(p, &allproc, p_list) {
589 if (p->p_tracep == vp) {
590 p->p_tracep = NULL;
591 p->p_traceflag = 0;
592 vnode_rele(vp);
593 }
594 }
595 }
596 }
597
598 /*
599 * Return true if caller has permission to set the ktracing state
600 * of target. Essentially, the target can't possess any
601 * more permissions than the caller. KTRFAC_ROOT signifies that
602 * root previously set the tracing status on the target process, and
603 * so, only root may further change it.
604 *
605 * TODO: check groups. use caller effective gid.
606 */
607 static int
608 ktrcanset(__unused struct proc *callp, struct proc *targetp)
609 {
610 kauth_cred_t caller = kauth_cred_get();
611 kauth_cred_t target = targetp->p_ucred; /* XXX */
612
613 #if 0
614 /* PRISON_CHECK was defined to 1 always .... */
615 if (!PRISON_CHECK(callp, targetp))
616 return (0);
617 #endif
618 if ((kauth_cred_getuid(caller) == target->cr_ruid &&
619 target->cr_ruid == target->cr_svuid &&
620 caller->cr_rgid == target->cr_rgid && /* XXX */
621 target->cr_rgid == target->cr_svgid &&
622 (targetp->p_traceflag & KTRFAC_ROOT) == 0 &&
623 (targetp->p_flag & P_SUGID) == 0) ||
624 !suser(caller, NULL))
625 return (1);
626
627 return (0);
628 }
629
630 #endif /* KTRACE */