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