1 | /*- |
2 | * Copyright (c) 1999-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
13 | * its contributors may be used to endorse or promote products derived |
14 | * from this software without specific prior written permission. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR |
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
25 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | /* |
29 | * NOTICE: This file was modified by McAfee Research in 2004 to introduce |
30 | * support for mandatory and extensible security protections. This notice |
31 | * is included in support of clause 2.2 (b) of the Apple Public License, |
32 | * Version 2.0. |
33 | */ |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/fcntl.h> |
37 | #include <sys/kernel.h> |
38 | #include <sys/lock.h> |
39 | #include <sys/namei.h> |
40 | #include <sys/proc_internal.h> |
41 | #include <sys/kauth.h> |
42 | #include <sys/queue.h> |
43 | #include <sys/systm.h> |
44 | #include <sys/time.h> |
45 | #include <sys/ucred.h> |
46 | #include <sys/uio.h> |
47 | #include <sys/unistd.h> |
48 | #include <sys/file_internal.h> |
49 | #include <sys/vnode_internal.h> |
50 | #include <sys/user.h> |
51 | #include <sys/syscall.h> |
52 | #include <sys/un.h> |
53 | #include <sys/sysent.h> |
54 | #include <sys/sysproto.h> |
55 | #include <sys/vfs_context.h> |
56 | #include <sys/domain.h> |
57 | #include <sys/protosw.h> |
58 | #include <sys/socketvar.h> |
59 | |
60 | #include <bsm/audit.h> |
61 | #include <bsm/audit_internal.h> |
62 | #include <bsm/audit_kevents.h> |
63 | |
64 | #include <security/audit/audit.h> |
65 | #include <security/audit/audit_bsd.h> |
66 | #include <security/audit/audit_private.h> |
67 | |
68 | #include <mach/host_priv.h> |
69 | #include <mach/host_special_ports.h> |
70 | #include <mach/audit_triggers_server.h> |
71 | |
72 | #include <kern/host.h> |
73 | #include <kern/sched_prim.h> |
74 | |
75 | #if CONFIG_MACF |
76 | #include <bsm/audit_record.h> |
77 | #include <security/mac.h> |
78 | #include <security/mac_framework.h> |
79 | #include <security/mac_policy.h> |
80 | #endif |
81 | |
82 | #include <net/route.h> |
83 | |
84 | #include <netinet/in.h> |
85 | #include <netinet/in_pcb.h> |
86 | |
87 | #include <IOKit/IOBSD.h> |
88 | |
89 | #if CONFIG_AUDIT |
90 | |
91 | #define IS_NOT_VALID_PID(p) ((p) < 1 || (p) > PID_MAX) |
92 | |
93 | #ifdef AUDIT_API_WARNINGS |
94 | /* |
95 | * Macro to warn about auditinfo_addr_t/auditpinfo_addr_t changing sizes |
96 | * to encourage the userland code to be recompiled and updated. |
97 | */ |
98 | #define WARN_IF_AINFO_ADDR_CHANGED(sz1, sz2, scall, tp) do { \ |
99 | if ((size_t)(sz1) != (size_t)(sz2)) { \ |
100 | char pn[MAXCOMLEN + 1]; \ |
101 | \ |
102 | proc_selfname(pn, MAXCOMLEN + 1); \ |
103 | printf("Size of %s used by %s in %s is different from " \ |
104 | "kernel's. Please recompile %s.\n", (tp), \ |
105 | (scall), pn, pn); \ |
106 | } \ |
107 | } while (0) |
108 | |
109 | /* |
110 | * Macro to warn about using ASID's outside the range [1 to PID_MAX] to |
111 | * encourage userland code changes. |
112 | */ |
113 | #define WARN_IF_BAD_ASID(asid, scall) do { \ |
114 | if (((asid) < 1 || (asid) > PID_MAX) && \ |
115 | (asid) != AU_ASSIGN_ASID) { \ |
116 | char pn[MAXCOMLEN + 1]; \ |
117 | \ |
118 | proc_selfname(pn, MAXCOMLEN + 1); \ |
119 | printf("%s in %s is using an ASID (%u) outside the " \ |
120 | "range [1 to %d]. Please change %s to use an ASID "\ |
121 | "within this range or use AU_ASSIGN_ASID.\n", \ |
122 | (scall), pn, (uint32_t)(asid), PID_MAX, pn); \ |
123 | } \ |
124 | } while (0) |
125 | |
126 | #else /* ! AUDIT_API_WARNINGS */ |
127 | |
128 | #define WARN_IF_AINFO_ADDR_CHANGED(sz1, sz2, scall, tp) do { \ |
129 | } while (0) |
130 | |
131 | #define WARN_IF_BAD_ASID(asid, scall) do { \ |
132 | } while (0) |
133 | |
134 | #endif /* AUDIT_API_WARNINGS */ |
135 | |
136 | /* |
137 | * System call to allow a user space application to submit a BSM audit record |
138 | * to the kernel for inclusion in the audit log. This function does little |
139 | * verification on the audit record that is submitted. |
140 | * |
141 | * XXXAUDIT: Audit preselection for user records does not currently work, |
142 | * since we pre-select only based on the AUE_audit event type, not the event |
143 | * type submitted as part of the user audit data. |
144 | */ |
145 | /* ARGSUSED */ |
146 | int |
147 | audit(proc_t p, struct audit_args *uap, __unused int32_t *retval) |
148 | { |
149 | int error = 0; |
150 | void * rec = NULL; |
151 | void * full_rec = NULL; |
152 | struct kaudit_record *ar = NULL; |
153 | struct uthread *uthr = NULL; |
154 | int add_identity_token = 1; |
155 | int max_record_length = MAX_AUDIT_RECORD_SIZE; |
156 | void *udata = NULL; |
157 | u_int ulen = 0; |
158 | struct au_identity_info id_info = { |
159 | .signer_type = 0, |
160 | .signing_id = NULL, |
161 | .signing_id_trunc = 0, |
162 | .team_id = NULL, |
163 | .team_id_trunc = 0, |
164 | .cdhash = NULL, |
165 | .cdhash_len = 0 |
166 | }; |
167 | token_t *id_tok = NULL; |
168 | boolean_t kern_events_allowed = FALSE; |
169 | char *signing_id = NULL; |
170 | char process_name[MAXCOMLEN + 1] = {}; |
171 | int signer_type = 0; |
172 | |
173 | error = suser(cred: kauth_cred_get(), acflag: &p->p_acflag); |
174 | if (error) { |
175 | /* |
176 | * If a process is not running as root but is properly |
177 | * entitled, allow it to audit non-kernel events only. |
178 | */ |
179 | if (!IOCurrentTaskHasEntitlement(AU_AUDIT_USER_ENTITLEMENT)) { |
180 | goto free_out; |
181 | } |
182 | } else { |
183 | kern_events_allowed = TRUE; |
184 | } |
185 | |
186 | mtx_lock(&audit_mtx); |
187 | max_record_length = MIN(audit_qctrl.aq_bufsz, MAX_AUDIT_RECORD_SIZE); |
188 | mtx_unlock(&audit_mtx); |
189 | |
190 | if (IOCurrentTaskHasEntitlement(AU_CLASS_RESERVED_ENTITLEMENT)) { |
191 | /* Entitled tasks are trusted to add appropriate identity info */ |
192 | add_identity_token = 0; |
193 | } else { |
194 | /* |
195 | * If the caller is unentitled, an identity token will be added and |
196 | * the space must be accounted for |
197 | */ |
198 | max_record_length -= MAX_AUDIT_IDENTITY_SIZE; |
199 | } |
200 | |
201 | if ((uap->length <= 0) || (uap->length > max_record_length)) { |
202 | error = EINVAL; |
203 | goto free_out; |
204 | } |
205 | |
206 | ar = currecord(); |
207 | |
208 | /* |
209 | * If there's no current audit record (audit() itself not audited) |
210 | * commit the user audit record. |
211 | */ |
212 | if (ar == NULL) { |
213 | uthr = curthread(); |
214 | if (uthr == NULL) { |
215 | /* can this happen? */ |
216 | error = ENOTSUP; |
217 | goto free_out; |
218 | } |
219 | |
220 | /* |
221 | * This is not very efficient; we're required to allocate a |
222 | * complete kernel audit record just so the user record can |
223 | * tag along. |
224 | */ |
225 | uthr->uu_ar = audit_new(AUE_NULL, p, td: uthr); |
226 | if (uthr->uu_ar == NULL) { |
227 | error = ENOTSUP; |
228 | goto free_out; |
229 | } |
230 | ar = uthr->uu_ar; |
231 | } |
232 | |
233 | rec = kalloc_data(uap->length, Z_WAITOK); |
234 | if (!rec) { |
235 | error = ENOMEM; |
236 | goto free_out; |
237 | } |
238 | |
239 | error = copyin(uap->record, rec, uap->length); |
240 | if (error) { |
241 | goto free_out; |
242 | } |
243 | |
244 | #if CONFIG_MACF |
245 | error = mac_system_check_audit(cred: kauth_cred_get(), record: rec, length: uap->length); |
246 | if (error) { |
247 | goto free_out; |
248 | } |
249 | #endif |
250 | |
251 | /* Verify the record. */ |
252 | if (bsm_rec_verify(rec, length: uap->length, kern_events_allowed) == 0) { |
253 | error = EINVAL; |
254 | goto free_out; |
255 | } |
256 | |
257 | if (add_identity_token) { |
258 | struct hdr_tok_partial *hdr; |
259 | struct trl_tok_partial *trl; |
260 | int bytes_copied = 0; |
261 | |
262 | /* Create a new identity token for this buffer */ |
263 | audit_identity_info_construct(id_info: &id_info); |
264 | id_tok = au_to_identity(signer_type: id_info.signer_type, signing_id: id_info.signing_id, |
265 | signing_id_trunc: id_info.signing_id_trunc, team_id: id_info.team_id, team_id_trunc: id_info.team_id_trunc, |
266 | cdhash: id_info.cdhash, cdhash_len: id_info.cdhash_len); |
267 | if (!id_tok) { |
268 | error = ENOMEM; |
269 | goto free_out; |
270 | } |
271 | |
272 | /* Splice the record together using a new buffer */ |
273 | full_rec = kalloc_data(uap->length + id_tok->len, Z_WAITOK); |
274 | if (!full_rec) { |
275 | error = ENOMEM; |
276 | goto free_out; |
277 | } |
278 | |
279 | signing_id = id_info.signing_id; |
280 | signer_type = id_info.signer_type; |
281 | |
282 | /* Copy the original buffer up to but not including the trailer */ |
283 | memcpy(dst: full_rec, src: rec, n: uap->length - AUDIT_TRAILER_SIZE); |
284 | bytes_copied = uap->length - AUDIT_TRAILER_SIZE; |
285 | |
286 | /* Copy the identity token */ |
287 | memcpy(dst: (void *)((uintptr_t)full_rec + bytes_copied), src: id_tok->t_data, n: id_tok->len); |
288 | bytes_copied += id_tok->len; |
289 | |
290 | /* Copy the old trailer */ |
291 | memcpy(dst: (void *)((uintptr_t)full_rec + bytes_copied), |
292 | src: (const void *)((uintptr_t)rec + (uap->length - AUDIT_TRAILER_SIZE)), |
293 | AUDIT_TRAILER_SIZE); |
294 | bytes_copied += AUDIT_TRAILER_SIZE; |
295 | |
296 | /* Fix the record size stored in the header token */ |
297 | hdr = (struct hdr_tok_partial*)full_rec; |
298 | hdr->len = htonl(bytes_copied); |
299 | |
300 | /* Fix the record size stored in the trailer token */ |
301 | trl = (struct trl_tok_partial*) |
302 | ((uintptr_t)full_rec + bytes_copied - AUDIT_TRAILER_SIZE); |
303 | trl->len = htonl(bytes_copied); |
304 | |
305 | udata = full_rec; |
306 | ulen = bytes_copied; |
307 | } else { |
308 | udata = rec; |
309 | ulen = uap->length; |
310 | } |
311 | |
312 | /* |
313 | * Attach the user audit record to the kernel audit record. Because |
314 | * this system call is an auditable event, we will write the user |
315 | * record along with the record for this audit event. |
316 | * |
317 | * XXXAUDIT: KASSERT appropriate starting values of k_udata, k_ulen, |
318 | * k_ar_commit & AR_COMMIT_USER? |
319 | */ |
320 | ar->k_udata = udata; |
321 | ar->k_ulen = ulen; |
322 | ar->k_ar_commit |= AR_COMMIT_USER; |
323 | |
324 | /* |
325 | * Currently we assume that all preselection has been performed in |
326 | * userspace. We unconditionally set these masks so that the records |
327 | * get committed both to the trail and pipe. In the future we will |
328 | * want to setup kernel based preselection. |
329 | */ |
330 | ar->k_ar_commit |= (AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE); |
331 | |
332 | // Send data for analytics for non-platform binaries only |
333 | if (signer_type == 0 && add_identity_token) { |
334 | proc_name(pid: proc_pid(p), buf: process_name, size: sizeof(process_name)); |
335 | (void)audit_send_analytics(id: signing_id, name: process_name); |
336 | } |
337 | |
338 | free_out: |
339 | /* |
340 | * If rec was allocated, it must be freed if an identity token was added |
341 | * (since full_rec will be used) OR there was an error (since nothing |
342 | * will be attached to the kernel structure). |
343 | */ |
344 | if (rec && (add_identity_token || error)) { |
345 | kfree_data_addr(rec); |
346 | } |
347 | |
348 | /* Only free full_rec if an error occurred */ |
349 | if (full_rec && error) { |
350 | kfree_data_addr(full_rec); |
351 | } |
352 | |
353 | audit_identity_info_destruct(id_info: &id_info); |
354 | if (id_tok) { |
355 | kfree_data(id_tok->t_data, id_tok->len); |
356 | kfree_type(struct au_token, id_tok); |
357 | } |
358 | |
359 | return error; |
360 | } |
361 | |
362 | /* |
363 | * System call to manipulate auditing. |
364 | */ |
365 | /* ARGSUSED */ |
366 | int |
367 | auditon(proc_t p, struct auditon_args *uap, __unused int32_t *retval) |
368 | { |
369 | kauth_cred_t scred; |
370 | int error = 0; |
371 | union auditon_udata udata; |
372 | proc_t tp = PROC_NULL; |
373 | struct auditinfo_addr aia; |
374 | |
375 | AUDIT_ARG(cmd, uap->cmd); |
376 | |
377 | #if CONFIG_MACF |
378 | error = mac_system_check_auditon(cred: kauth_cred_get(), cmd: uap->cmd); |
379 | if (error) { |
380 | return error; |
381 | } |
382 | #endif |
383 | |
384 | if ((uap->length <= 0) || (uap->length > |
385 | (int)sizeof(union auditon_udata))) { |
386 | return EINVAL; |
387 | } |
388 | |
389 | memset(s: (void *)&udata, c: 0, n: sizeof(udata)); |
390 | |
391 | /* |
392 | * Some of the GET commands use the arguments too. |
393 | */ |
394 | switch (uap->cmd) { |
395 | case A_SETPOLICY: |
396 | case A_OLDSETPOLICY: |
397 | case A_SETKMASK: |
398 | case A_SETQCTRL: |
399 | case A_OLDSETQCTRL: |
400 | case A_SETSTAT: |
401 | case A_SETUMASK: |
402 | case A_SETSMASK: |
403 | case A_SETCOND: |
404 | case A_OLDSETCOND: |
405 | case A_SETCLASS: |
406 | case A_SETPMASK: |
407 | case A_SETFSIZE: |
408 | case A_SETKAUDIT: |
409 | case A_GETCLASS: |
410 | case A_GETPINFO: |
411 | case A_GETPINFO_ADDR: |
412 | case A_SENDTRIGGER: |
413 | case A_GETSINFO_ADDR: |
414 | case A_GETSFLAGS: |
415 | case A_SETSFLAGS: |
416 | case A_SETCTLMODE: |
417 | case A_SETEXPAFTER: |
418 | error = copyin(uap->data, (void *)&udata, uap->length); |
419 | if (error) { |
420 | return error; |
421 | } |
422 | AUDIT_ARG(auditon, &udata); |
423 | AUDIT_ARG(len, uap->length); |
424 | break; |
425 | } |
426 | |
427 | /* Check appropriate privilege. */ |
428 | switch (uap->cmd) { |
429 | /* |
430 | * A_GETSINFO doesn't require priviledge but only superuser |
431 | * gets to see the audit masks. |
432 | */ |
433 | case A_GETSINFO_ADDR: |
434 | if ((sizeof(udata.au_kau_info) != uap->length) || |
435 | (audit_session_lookup(asid: udata.au_kau_info.ai_asid, |
436 | ret_aia: &udata.au_kau_info) != 0)) { |
437 | error = EINVAL; |
438 | } else if (!kauth_cred_issuser(cred: kauth_cred_get())) { |
439 | udata.au_kau_info.ai_mask.am_success = ~0; |
440 | udata.au_kau_info.ai_mask.am_failure = ~0; |
441 | } |
442 | break; |
443 | case A_GETSFLAGS: |
444 | case A_SETSFLAGS: |
445 | /* Getting one's own audit session flags requires no |
446 | * privilege. Setting the flags is subject to access |
447 | * control implemented in audit_session_setaia(). |
448 | */ |
449 | break; |
450 | case A_SETCTLMODE: |
451 | case A_SETEXPAFTER: |
452 | if (!IOCurrentTaskHasEntitlement(AU_CLASS_RESERVED_ENTITLEMENT)) { |
453 | error = EPERM; |
454 | } |
455 | break; |
456 | default: |
457 | error = suser(cred: kauth_cred_get(), acflag: &p->p_acflag); |
458 | break; |
459 | } |
460 | if (error) { |
461 | return error; |
462 | } |
463 | |
464 | /* |
465 | * If the audit subsytem is in external control mode, additional |
466 | * privilege checks are required for a subset of auditon commands |
467 | */ |
468 | if (audit_ctl_mode == AUDIT_CTLMODE_EXTERNAL) { |
469 | switch (uap->cmd) { |
470 | case A_SETCOND: |
471 | case A_SETFSIZE: |
472 | case A_SETPOLICY: |
473 | case A_SETQCTRL: |
474 | if (!IOCurrentTaskHasEntitlement(AU_CLASS_RESERVED_ENTITLEMENT)) { |
475 | error = EPERM; |
476 | } |
477 | break; |
478 | } |
479 | if (error) { |
480 | return error; |
481 | } |
482 | } |
483 | |
484 | /* |
485 | * XXX Need to implement these commands by accessing the global |
486 | * values associated with the commands. |
487 | */ |
488 | switch (uap->cmd) { |
489 | case A_OLDGETPOLICY: |
490 | case A_GETPOLICY: |
491 | if (sizeof(udata.au_policy64) == uap->length) { |
492 | mtx_lock(&audit_mtx); |
493 | if (!audit_fail_stop) { |
494 | udata.au_policy64 |= AUDIT_CNT; |
495 | } |
496 | if (audit_panic_on_write_fail) { |
497 | udata.au_policy64 |= AUDIT_AHLT; |
498 | } |
499 | if (audit_argv) { |
500 | udata.au_policy64 |= AUDIT_ARGV; |
501 | } |
502 | if (audit_arge) { |
503 | udata.au_policy64 |= AUDIT_ARGE; |
504 | } |
505 | mtx_unlock(&audit_mtx); |
506 | break; |
507 | } |
508 | if (sizeof(udata.au_policy) != uap->length) { |
509 | return EINVAL; |
510 | } |
511 | mtx_lock(&audit_mtx); |
512 | if (!audit_fail_stop) { |
513 | udata.au_policy |= AUDIT_CNT; |
514 | } |
515 | if (audit_panic_on_write_fail) { |
516 | udata.au_policy |= AUDIT_AHLT; |
517 | } |
518 | if (audit_argv) { |
519 | udata.au_policy |= AUDIT_ARGV; |
520 | } |
521 | if (audit_arge) { |
522 | udata.au_policy |= AUDIT_ARGE; |
523 | } |
524 | mtx_unlock(&audit_mtx); |
525 | break; |
526 | |
527 | case A_OLDSETPOLICY: |
528 | case A_SETPOLICY: |
529 | if (sizeof(udata.au_policy64) == uap->length) { |
530 | if (udata.au_policy64 & ~(AUDIT_CNT | AUDIT_AHLT | |
531 | AUDIT_ARGV | AUDIT_ARGE)) { |
532 | return EINVAL; |
533 | } |
534 | mtx_lock(&audit_mtx); |
535 | audit_fail_stop = ((udata.au_policy64 & AUDIT_CNT) == |
536 | 0); |
537 | audit_panic_on_write_fail = (udata.au_policy64 & |
538 | AUDIT_AHLT); |
539 | audit_argv = (udata.au_policy64 & AUDIT_ARGV); |
540 | audit_arge = (udata.au_policy64 & AUDIT_ARGE); |
541 | mtx_unlock(&audit_mtx); |
542 | break; |
543 | } |
544 | if ((sizeof(udata.au_policy) != uap->length) || |
545 | (udata.au_policy & ~(AUDIT_CNT | AUDIT_AHLT | AUDIT_ARGV | |
546 | AUDIT_ARGE))) { |
547 | return EINVAL; |
548 | } |
549 | /* |
550 | * XXX - Need to wake up waiters if the policy relaxes? |
551 | */ |
552 | mtx_lock(&audit_mtx); |
553 | audit_fail_stop = ((udata.au_policy & AUDIT_CNT) == 0); |
554 | audit_panic_on_write_fail = (udata.au_policy & AUDIT_AHLT); |
555 | audit_argv = (udata.au_policy & AUDIT_ARGV); |
556 | audit_arge = (udata.au_policy & AUDIT_ARGE); |
557 | mtx_unlock(&audit_mtx); |
558 | break; |
559 | |
560 | case A_GETKMASK: |
561 | if (sizeof(udata.au_mask) != uap->length) { |
562 | return EINVAL; |
563 | } |
564 | mtx_lock(&audit_mtx); |
565 | udata.au_mask = audit_nae_mask; |
566 | mtx_unlock(&audit_mtx); |
567 | break; |
568 | |
569 | case A_SETKMASK: |
570 | if (sizeof(udata.au_mask) != uap->length) { |
571 | return EINVAL; |
572 | } |
573 | mtx_lock(&audit_mtx); |
574 | audit_nae_mask = udata.au_mask; |
575 | AUDIT_CHECK_IF_KEVENTS_MASK(audit_nae_mask); |
576 | mtx_unlock(&audit_mtx); |
577 | break; |
578 | |
579 | case A_OLDGETQCTRL: |
580 | case A_GETQCTRL: |
581 | if (sizeof(udata.au_qctrl64) == uap->length) { |
582 | mtx_lock(&audit_mtx); |
583 | udata.au_qctrl64.aq64_hiwater = |
584 | (u_int64_t)audit_qctrl.aq_hiwater; |
585 | udata.au_qctrl64.aq64_lowater = |
586 | (u_int64_t)audit_qctrl.aq_lowater; |
587 | udata.au_qctrl64.aq64_bufsz = |
588 | (u_int64_t)audit_qctrl.aq_bufsz; |
589 | udata.au_qctrl64.aq64_delay = |
590 | (u_int64_t)audit_qctrl.aq_delay; |
591 | udata.au_qctrl64.aq64_minfree = |
592 | (int64_t)audit_qctrl.aq_minfree; |
593 | mtx_unlock(&audit_mtx); |
594 | break; |
595 | } |
596 | if (sizeof(udata.au_qctrl) != uap->length) { |
597 | return EINVAL; |
598 | } |
599 | mtx_lock(&audit_mtx); |
600 | udata.au_qctrl = audit_qctrl; |
601 | mtx_unlock(&audit_mtx); |
602 | break; |
603 | |
604 | case A_OLDSETQCTRL: |
605 | case A_SETQCTRL: |
606 | if (sizeof(udata.au_qctrl64) == uap->length) { |
607 | if ((udata.au_qctrl64.aq64_hiwater > AQ_MAXHIGH) || |
608 | (udata.au_qctrl64.aq64_lowater >= |
609 | udata.au_qctrl64.aq64_hiwater) || |
610 | (udata.au_qctrl64.aq64_bufsz > AQ_MAXBUFSZ) || |
611 | (udata.au_qctrl64.aq64_minfree < 0) || |
612 | (udata.au_qctrl64.aq64_minfree > 100)) { |
613 | return EINVAL; |
614 | } |
615 | mtx_lock(&audit_mtx); |
616 | audit_qctrl.aq_hiwater = |
617 | (int)udata.au_qctrl64.aq64_hiwater; |
618 | audit_qctrl.aq_lowater = |
619 | (int)udata.au_qctrl64.aq64_lowater; |
620 | audit_qctrl.aq_bufsz = |
621 | (int)udata.au_qctrl64.aq64_bufsz; |
622 | audit_qctrl.aq_minfree = |
623 | (int)udata.au_qctrl64.aq64_minfree; |
624 | audit_qctrl.aq_delay = -1; /* Not used. */ |
625 | mtx_unlock(&audit_mtx); |
626 | break; |
627 | } |
628 | if ((sizeof(udata.au_qctrl) != uap->length) || |
629 | (udata.au_qctrl.aq_hiwater > AQ_MAXHIGH) || |
630 | (udata.au_qctrl.aq_lowater >= udata.au_qctrl.aq_hiwater) || |
631 | (udata.au_qctrl.aq_bufsz > AQ_MAXBUFSZ) || |
632 | (udata.au_qctrl.aq_minfree < 0) || |
633 | (udata.au_qctrl.aq_minfree > 100)) { |
634 | return EINVAL; |
635 | } |
636 | |
637 | mtx_lock(&audit_mtx); |
638 | audit_qctrl = udata.au_qctrl; |
639 | /* XXX The queue delay value isn't used with the kernel. */ |
640 | audit_qctrl.aq_delay = -1; |
641 | mtx_unlock(&audit_mtx); |
642 | break; |
643 | |
644 | case A_GETCWD: |
645 | return ENOSYS; |
646 | |
647 | case A_GETCAR: |
648 | return ENOSYS; |
649 | |
650 | case A_GETSTAT: |
651 | return ENOSYS; |
652 | |
653 | case A_SETSTAT: |
654 | return ENOSYS; |
655 | |
656 | case A_SETUMASK: |
657 | return ENOSYS; |
658 | |
659 | case A_SETSMASK: |
660 | return ENOSYS; |
661 | |
662 | case A_OLDGETCOND: |
663 | case A_GETCOND: |
664 | if (sizeof(udata.au_cond64) == uap->length) { |
665 | mtx_lock(&audit_mtx); |
666 | if (audit_enabled && !audit_suspended) { |
667 | udata.au_cond64 = AUC_AUDITING; |
668 | } else { |
669 | udata.au_cond64 = AUC_NOAUDIT; |
670 | } |
671 | mtx_unlock(&audit_mtx); |
672 | break; |
673 | } |
674 | if (sizeof(udata.au_cond) != uap->length) { |
675 | return EINVAL; |
676 | } |
677 | mtx_lock(&audit_mtx); |
678 | if (audit_enabled && !audit_suspended) { |
679 | udata.au_cond = AUC_AUDITING; |
680 | } else { |
681 | udata.au_cond = AUC_NOAUDIT; |
682 | } |
683 | mtx_unlock(&audit_mtx); |
684 | break; |
685 | |
686 | case A_OLDSETCOND: |
687 | case A_SETCOND: |
688 | if (sizeof(udata.au_cond64) == uap->length) { |
689 | mtx_lock(&audit_mtx); |
690 | if (udata.au_cond64 == AUC_NOAUDIT) { |
691 | audit_suspended = 1; |
692 | } |
693 | if (udata.au_cond64 == AUC_AUDITING) { |
694 | audit_suspended = 0; |
695 | } |
696 | if (udata.au_cond64 == AUC_DISABLED) { |
697 | audit_suspended = 1; |
698 | mtx_unlock(&audit_mtx); |
699 | audit_shutdown(); |
700 | break; |
701 | } |
702 | mtx_unlock(&audit_mtx); |
703 | break; |
704 | } |
705 | if (sizeof(udata.au_cond) != uap->length) { |
706 | return EINVAL; |
707 | } |
708 | mtx_lock(&audit_mtx); |
709 | if (udata.au_cond == AUC_NOAUDIT) { |
710 | audit_suspended = 1; |
711 | } |
712 | if (udata.au_cond == AUC_AUDITING) { |
713 | audit_suspended = 0; |
714 | } |
715 | if (udata.au_cond == AUC_DISABLED) { |
716 | audit_suspended = 1; |
717 | mtx_unlock(&audit_mtx); |
718 | audit_shutdown(); |
719 | break; |
720 | } |
721 | mtx_unlock(&audit_mtx); |
722 | break; |
723 | |
724 | case A_GETCLASS: |
725 | if (sizeof(udata.au_evclass) != uap->length) { |
726 | return EINVAL; |
727 | } |
728 | udata.au_evclass.ec_class = au_event_class( |
729 | event: udata.au_evclass.ec_number); |
730 | break; |
731 | |
732 | case A_SETCLASS: |
733 | if (sizeof(udata.au_evclass) != uap->length) { |
734 | return EINVAL; |
735 | } |
736 | au_evclassmap_insert(event: udata.au_evclass.ec_number, |
737 | class: udata.au_evclass.ec_class); |
738 | break; |
739 | |
740 | case A_GETPINFO: |
741 | if ((sizeof(udata.au_aupinfo) != uap->length) || |
742 | IS_NOT_VALID_PID(udata.au_aupinfo.ap_pid)) { |
743 | return EINVAL; |
744 | } |
745 | |
746 | scred = kauth_cred_proc_ref_for_pid(pid: udata.au_aupinfo.ap_pid); |
747 | if (scred == NOCRED) { |
748 | return ESRCH; |
749 | } |
750 | |
751 | if (scred->cr_audit.as_aia_p->ai_termid.at_type == AU_IPv6) { |
752 | kauth_cred_unref(&scred); |
753 | return EINVAL; |
754 | } |
755 | |
756 | udata.au_aupinfo.ap_auid = |
757 | scred->cr_audit.as_aia_p->ai_auid; |
758 | udata.au_aupinfo.ap_mask.am_success = |
759 | scred->cr_audit.as_mask.am_success; |
760 | udata.au_aupinfo.ap_mask.am_failure = |
761 | scred->cr_audit.as_mask.am_failure; |
762 | udata.au_aupinfo.ap_termid.machine = |
763 | scred->cr_audit.as_aia_p->ai_termid.at_addr[0]; |
764 | udata.au_aupinfo.ap_termid.port = |
765 | scred->cr_audit.as_aia_p->ai_termid.at_port; |
766 | udata.au_aupinfo.ap_asid = |
767 | scred->cr_audit.as_aia_p->ai_asid; |
768 | |
769 | kauth_cred_unref(&scred); |
770 | break; |
771 | |
772 | case A_SETPMASK: |
773 | if ((sizeof(udata.au_aupinfo) != uap->length) || |
774 | IS_NOT_VALID_PID(udata.au_aupinfo.ap_pid)) { |
775 | return EINVAL; |
776 | } |
777 | if ((tp = proc_find(pid: udata.au_aupinfo.ap_pid)) == NULL) { |
778 | return ESRCH; |
779 | } |
780 | |
781 | smr_proc_task_enter(); |
782 | scred = proc_ucred_smr(p: tp); |
783 | bcopy(src: scred->cr_audit.as_aia_p, dst: &aia, n: sizeof(aia)); |
784 | scred = NOCRED; |
785 | smr_proc_task_leave(); |
786 | |
787 | aia.ai_mask.am_success = |
788 | udata.au_aupinfo.ap_mask.am_success; |
789 | aia.ai_mask.am_failure = |
790 | udata.au_aupinfo.ap_mask.am_failure; |
791 | AUDIT_CHECK_IF_KEVENTS_MASK(aia.ai_mask); |
792 | error = audit_session_setaia(p: tp, aia_p: &aia); |
793 | proc_rele(p: tp); |
794 | tp = PROC_NULL; |
795 | if (error) { |
796 | return error; |
797 | } |
798 | break; |
799 | |
800 | case A_SETFSIZE: |
801 | if ((sizeof(udata.au_fstat) != uap->length) || |
802 | ((udata.au_fstat.af_filesz != 0) && |
803 | (udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE))) { |
804 | return EINVAL; |
805 | } |
806 | mtx_lock(&audit_mtx); |
807 | audit_fstat.af_filesz = udata.au_fstat.af_filesz; |
808 | mtx_unlock(&audit_mtx); |
809 | break; |
810 | |
811 | case A_GETFSIZE: |
812 | if (sizeof(udata.au_fstat) != uap->length) { |
813 | return EINVAL; |
814 | } |
815 | mtx_lock(&audit_mtx); |
816 | udata.au_fstat.af_filesz = audit_fstat.af_filesz; |
817 | udata.au_fstat.af_currsz = audit_fstat.af_currsz; |
818 | mtx_unlock(&audit_mtx); |
819 | break; |
820 | |
821 | case A_GETPINFO_ADDR: |
822 | if ((sizeof(udata.au_aupinfo_addr) != uap->length) || |
823 | IS_NOT_VALID_PID(udata.au_aupinfo_addr.ap_pid)) { |
824 | return EINVAL; |
825 | } |
826 | scred = kauth_cred_proc_ref_for_pid(pid: udata.au_aupinfo.ap_pid); |
827 | if (scred == NOCRED) { |
828 | return ESRCH; |
829 | } |
830 | |
831 | WARN_IF_AINFO_ADDR_CHANGED(uap->length, |
832 | sizeof(auditpinfo_addr_t), "auditon(A_GETPINFO_ADDR,...)" , |
833 | "auditpinfo_addr_t" ); |
834 | |
835 | udata.au_aupinfo_addr.ap_auid = |
836 | scred->cr_audit.as_aia_p->ai_auid; |
837 | udata.au_aupinfo_addr.ap_asid = |
838 | scred->cr_audit.as_aia_p->ai_asid; |
839 | udata.au_aupinfo_addr.ap_mask.am_success = |
840 | scred->cr_audit.as_mask.am_success; |
841 | udata.au_aupinfo_addr.ap_mask.am_failure = |
842 | scred->cr_audit.as_mask.am_failure; |
843 | bcopy(src: &scred->cr_audit.as_aia_p->ai_termid, |
844 | dst: &udata.au_aupinfo_addr.ap_termid, |
845 | n: sizeof(au_tid_addr_t)); |
846 | udata.au_aupinfo_addr.ap_flags = |
847 | scred->cr_audit.as_aia_p->ai_flags; |
848 | |
849 | kauth_cred_unref(&scred); |
850 | break; |
851 | |
852 | case A_GETKAUDIT: |
853 | if (sizeof(udata.au_kau_info) != uap->length) { |
854 | return EINVAL; |
855 | } |
856 | audit_get_kinfo(&udata.au_kau_info); |
857 | break; |
858 | |
859 | case A_SETKAUDIT: |
860 | if ((sizeof(udata.au_kau_info) != uap->length) || |
861 | (udata.au_kau_info.ai_termid.at_type != AU_IPv4 && |
862 | udata.au_kau_info.ai_termid.at_type != AU_IPv6)) { |
863 | return EINVAL; |
864 | } |
865 | audit_set_kinfo(&udata.au_kau_info); |
866 | break; |
867 | |
868 | case A_SENDTRIGGER: |
869 | if ((sizeof(udata.au_trigger) != uap->length) || |
870 | (udata.au_trigger < AUDIT_TRIGGER_MIN) || |
871 | (udata.au_trigger > AUDIT_TRIGGER_MAX)) { |
872 | return EINVAL; |
873 | } |
874 | return audit_send_trigger(trigger: udata.au_trigger); |
875 | |
876 | case A_GETSINFO_ADDR: |
877 | /* Handled above before switch(). */ |
878 | break; |
879 | |
880 | case A_GETSFLAGS: |
881 | if (sizeof(udata.au_flags) != uap->length) { |
882 | return EINVAL; |
883 | } |
884 | bcopy(src: &(kauth_cred_get()->cr_audit.as_aia_p->ai_flags), |
885 | dst: &udata.au_flags, n: sizeof(udata.au_flags)); |
886 | break; |
887 | |
888 | case A_SETSFLAGS: |
889 | if (sizeof(udata.au_flags) != uap->length) { |
890 | return EINVAL; |
891 | } |
892 | scred = kauth_cred_get(); |
893 | bcopy(src: scred->cr_audit.as_aia_p, dst: &aia, n: sizeof(aia)); |
894 | bcopy(src: &scred->cr_audit.as_mask, dst: &aia.ai_mask, n: sizeof(au_mask_t)); |
895 | aia.ai_flags = udata.au_flags; |
896 | error = audit_session_setaia(p, aia_p: &aia); |
897 | if (error) { |
898 | return error; |
899 | } |
900 | break; |
901 | |
902 | case A_GETCTLMODE: |
903 | if (sizeof(udata.au_ctl_mode) != uap->length) { |
904 | return EINVAL; |
905 | } |
906 | mtx_lock(&audit_mtx); |
907 | udata.au_ctl_mode = audit_ctl_mode; |
908 | mtx_unlock(&audit_mtx); |
909 | break; |
910 | |
911 | case A_SETCTLMODE: |
912 | if (sizeof(udata.au_ctl_mode) != uap->length) { |
913 | return EINVAL; |
914 | } |
915 | |
916 | mtx_lock(&audit_mtx); |
917 | |
918 | if (udata.au_ctl_mode == AUDIT_CTLMODE_NORMAL) { |
919 | audit_ctl_mode = AUDIT_CTLMODE_NORMAL; |
920 | } else if (udata.au_ctl_mode == AUDIT_CTLMODE_EXTERNAL) { |
921 | audit_ctl_mode = AUDIT_CTLMODE_EXTERNAL; |
922 | } else { |
923 | mtx_unlock(&audit_mtx); |
924 | return EINVAL; |
925 | } |
926 | |
927 | mtx_unlock(&audit_mtx); |
928 | break; |
929 | |
930 | case A_GETEXPAFTER: |
931 | if (sizeof(udata.au_expire_after) != uap->length) { |
932 | return EINVAL; |
933 | } |
934 | mtx_lock(&audit_mtx); |
935 | udata.au_expire_after.age = audit_expire_after.age; |
936 | udata.au_expire_after.size = audit_expire_after.size; |
937 | udata.au_expire_after.op_type = audit_expire_after.op_type; |
938 | mtx_unlock(&audit_mtx); |
939 | break; |
940 | |
941 | case A_SETEXPAFTER: |
942 | if (sizeof(udata.au_expire_after) != uap->length) { |
943 | return EINVAL; |
944 | } |
945 | mtx_lock(&audit_mtx); |
946 | audit_expire_after.age = udata.au_expire_after.age; |
947 | audit_expire_after.size = udata.au_expire_after.size; |
948 | audit_expire_after.op_type = udata.au_expire_after.op_type; |
949 | mtx_unlock(&audit_mtx); |
950 | break; |
951 | |
952 | default: |
953 | return EINVAL; |
954 | } |
955 | |
956 | /* |
957 | * Copy data back to userspace for the GET comands. |
958 | */ |
959 | switch (uap->cmd) { |
960 | case A_GETPOLICY: |
961 | case A_OLDGETPOLICY: |
962 | case A_GETKMASK: |
963 | case A_GETQCTRL: |
964 | case A_OLDGETQCTRL: |
965 | case A_GETCWD: |
966 | case A_GETCAR: |
967 | case A_GETSTAT: |
968 | case A_GETCOND: |
969 | case A_OLDGETCOND: |
970 | case A_GETCLASS: |
971 | case A_GETPINFO: |
972 | case A_GETFSIZE: |
973 | case A_GETPINFO_ADDR: |
974 | case A_GETKAUDIT: |
975 | case A_GETSINFO_ADDR: |
976 | case A_GETSFLAGS: |
977 | case A_GETCTLMODE: |
978 | case A_GETEXPAFTER: |
979 | error = copyout((void *)&udata, uap->data, uap->length); |
980 | if (error) { |
981 | return ENOSYS; |
982 | } |
983 | break; |
984 | } |
985 | |
986 | return 0; |
987 | } |
988 | |
989 | /* |
990 | * System calls to manage the user audit information. |
991 | */ |
992 | /* ARGSUSED */ |
993 | int |
994 | getauid(proc_t p, struct getauid_args *uap, __unused int32_t *retval) |
995 | { |
996 | au_id_t id; |
997 | int error; |
998 | |
999 | #if CONFIG_MACF |
1000 | error = mac_proc_check_getauid(proc: p); |
1001 | if (error) { |
1002 | return error; |
1003 | } |
1004 | #endif |
1005 | id = current_cached_proc_cred(p)->cr_audit.as_aia_p->ai_auid; |
1006 | |
1007 | error = copyout((void *)&id, uap->auid, sizeof(id)); |
1008 | if (error) { |
1009 | return error; |
1010 | } |
1011 | |
1012 | return 0; |
1013 | } |
1014 | |
1015 | /* ARGSUSED */ |
1016 | int |
1017 | setauid(proc_t p, struct setauid_args *uap, __unused int32_t *retval) |
1018 | { |
1019 | int error; |
1020 | au_id_t id; |
1021 | kauth_cred_t scred; |
1022 | struct auditinfo_addr aia; |
1023 | |
1024 | error = copyin(uap->auid, &id, sizeof(id)); |
1025 | if (error) { |
1026 | return error; |
1027 | } |
1028 | AUDIT_ARG(auid, id); |
1029 | |
1030 | #if CONFIG_MACF |
1031 | error = mac_proc_check_setauid(proc: p, auid: id); |
1032 | if (error) { |
1033 | return error; |
1034 | } |
1035 | #endif |
1036 | |
1037 | scred = current_cached_proc_cred(p); |
1038 | error = suser(cred: scred, acflag: &p->p_acflag); |
1039 | if (error) { |
1040 | return error; |
1041 | } |
1042 | |
1043 | bcopy(src: scred->cr_audit.as_aia_p, dst: &aia, n: sizeof(aia)); |
1044 | if (aia.ai_asid == AU_DEFAUDITSID) { |
1045 | aia.ai_asid = AU_ASSIGN_ASID; |
1046 | } |
1047 | bcopy(src: &scred->cr_audit.as_mask, dst: &aia.ai_mask, n: sizeof(au_mask_t)); |
1048 | |
1049 | aia.ai_auid = id; |
1050 | error = audit_session_setaia(p, aia_p: &aia); |
1051 | |
1052 | return error; |
1053 | } |
1054 | |
1055 | static int |
1056 | getaudit_addr_internal(proc_t p, kauth_cred_t scred, user_addr_t user_addr, size_t length) |
1057 | { |
1058 | auditinfo_addr_t aia; |
1059 | |
1060 | bcopy(src: scred->cr_audit.as_aia_p, dst: &aia, n: sizeof(auditinfo_addr_t)); |
1061 | |
1062 | /* |
1063 | * Only superuser gets to see the real mask. |
1064 | */ |
1065 | if (suser(cred: scred, acflag: &p->p_acflag)) { |
1066 | aia.ai_mask.am_success = ~0; |
1067 | aia.ai_mask.am_failure = ~0; |
1068 | } else { |
1069 | bcopy(src: &scred->cr_audit.as_mask, dst: &aia.ai_mask, n: sizeof(au_mask_t)); |
1070 | } |
1071 | |
1072 | return copyout(&aia, user_addr, min(sizeof(aia), length)); |
1073 | } |
1074 | |
1075 | /* ARGSUSED */ |
1076 | int |
1077 | getaudit_addr(proc_t p, struct getaudit_addr_args *uap, |
1078 | __unused int32_t *retval) |
1079 | { |
1080 | kauth_cred_t scred; |
1081 | #if CONFIG_MACF |
1082 | int error = mac_proc_check_getaudit(proc: p); |
1083 | |
1084 | if (error) { |
1085 | return error; |
1086 | } |
1087 | #endif /* CONFIG_MACF */ |
1088 | WARN_IF_AINFO_ADDR_CHANGED(uap->length, sizeof(auditinfo_addr_t), |
1089 | "getaudit_addr(2)" , "auditinfo_addr_t" ); |
1090 | |
1091 | scred = current_cached_proc_cred(p); |
1092 | return getaudit_addr_internal(p, scred, user_addr: uap->auditinfo_addr, length: uap->length); |
1093 | } |
1094 | |
1095 | /* ARGSUSED */ |
1096 | int |
1097 | setaudit_addr(proc_t p, struct setaudit_addr_args *uap, |
1098 | __unused int32_t *retval) |
1099 | { |
1100 | struct auditinfo_addr aia; |
1101 | int error; |
1102 | |
1103 | bzero(s: &aia, n: sizeof(auditinfo_addr_t)); |
1104 | error = copyin(uap->auditinfo_addr, &aia, |
1105 | min(sizeof(aia), uap->length)); |
1106 | if (error) { |
1107 | return error; |
1108 | } |
1109 | AUDIT_ARG(auditinfo_addr, &aia); |
1110 | if (aia.ai_termid.at_type != AU_IPv6 && |
1111 | aia.ai_termid.at_type != AU_IPv4) { |
1112 | return EINVAL; |
1113 | } |
1114 | if (aia.ai_asid != AU_ASSIGN_ASID && |
1115 | (uint32_t)aia.ai_asid > ASSIGNED_ASID_MAX) { |
1116 | return EINVAL; |
1117 | } |
1118 | |
1119 | #if CONFIG_MACF |
1120 | error = mac_proc_check_setaudit(proc: p, ai: &aia); |
1121 | if (error) { |
1122 | return error; |
1123 | } |
1124 | #endif |
1125 | |
1126 | error = suser(cred: current_cached_proc_cred(p), acflag: &p->p_acflag); |
1127 | if (error) { |
1128 | return error; |
1129 | } |
1130 | |
1131 | WARN_IF_AINFO_ADDR_CHANGED(uap->length, sizeof(auditinfo_addr_t), |
1132 | "setaudit_addr(2)" , "auditinfo_addr_t" ); |
1133 | WARN_IF_BAD_ASID(aia.ai_asid, "setaudit_addr(2)" ); |
1134 | |
1135 | AUDIT_CHECK_IF_KEVENTS_MASK(aia.ai_mask); |
1136 | if (aia.ai_asid == AU_DEFAUDITSID) { |
1137 | aia.ai_asid = AU_ASSIGN_ASID; |
1138 | } |
1139 | |
1140 | error = audit_session_setaia(p, aia_p: &aia); |
1141 | if (error) { |
1142 | return error; |
1143 | } |
1144 | |
1145 | /* |
1146 | * If asked to assign an ASID then let the user know what the ASID is |
1147 | * by copying the auditinfo_addr struct back out. |
1148 | * |
1149 | * Note: because we just updated the proc cred, we can't use |
1150 | * current_cached_proc_cred_ref() here. |
1151 | */ |
1152 | if (aia.ai_asid == AU_ASSIGN_ASID) { |
1153 | kauth_cred_t scred; |
1154 | |
1155 | scred = kauth_cred_proc_ref(procp: p); |
1156 | error = getaudit_addr_internal(p, scred, user_addr: uap->auditinfo_addr, |
1157 | length: uap->length); |
1158 | kauth_cred_unref(&scred); |
1159 | } |
1160 | |
1161 | return error; |
1162 | } |
1163 | |
1164 | /* |
1165 | * Syscall to manage audit files. |
1166 | * |
1167 | */ |
1168 | /* ARGSUSED */ |
1169 | int |
1170 | auditctl(proc_t p, struct auditctl_args *uap, __unused int32_t *retval) |
1171 | { |
1172 | struct nameidata nd; |
1173 | kauth_cred_t cred; |
1174 | struct vnode *vp; |
1175 | int error = 0; |
1176 | au_ctlmode_t ctlmode; |
1177 | |
1178 | error = suser(cred: kauth_cred_get(), acflag: &p->p_acflag); |
1179 | if (error) { |
1180 | return error; |
1181 | } |
1182 | |
1183 | ctlmode = audit_ctl_mode; |
1184 | |
1185 | /* |
1186 | * Do not allow setting of a path when auditing is in reserved mode |
1187 | */ |
1188 | if (ctlmode == AUDIT_CTLMODE_EXTERNAL && |
1189 | !IOCurrentTaskHasEntitlement(AU_AUDITCTL_RESERVED_ENTITLEMENT)) { |
1190 | return EPERM; |
1191 | } |
1192 | |
1193 | vp = NULL; |
1194 | cred = NULL; |
1195 | |
1196 | /* |
1197 | * If a path is specified, open the replacement vnode, perform |
1198 | * validity checks, and grab another reference to the current |
1199 | * credential. |
1200 | * |
1201 | * XXX Changes API slightly. NULL path no longer disables audit but |
1202 | * returns EINVAL. |
1203 | */ |
1204 | if (uap->path == USER_ADDR_NULL) { |
1205 | return EINVAL; |
1206 | } |
1207 | |
1208 | NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW | LOCKLEAF | AUDITVNPATH1, |
1209 | (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : |
1210 | UIO_USERSPACE32), uap->path, vfs_context_current()); |
1211 | error = vn_open(ndp: &nd, AUDIT_OPEN_FLAGS, cmode: 0); |
1212 | if (error) { |
1213 | return error; |
1214 | } |
1215 | vp = nd.ni_vp; |
1216 | #if CONFIG_MACF |
1217 | /* |
1218 | * Accessibility of the vnode was determined in vn_open; the |
1219 | * mac_system_check_auditctl should only determine whether that vnode |
1220 | * is appropriate for storing audit data, or that the caller was |
1221 | * permitted to control the auditing system at all. For example, a |
1222 | * confidentiality policy may want to ensure that audit files are |
1223 | * always high sensitivity. |
1224 | */ |
1225 | error = mac_system_check_auditctl(cred: kauth_cred_get(), vp); |
1226 | if (error) { |
1227 | vn_close(vp, AUDIT_CLOSE_FLAGS, ctx: vfs_context_current()); |
1228 | vnode_put(vp); |
1229 | return error; |
1230 | } |
1231 | #endif |
1232 | if (vp->v_type != VREG) { |
1233 | vn_close(vp, AUDIT_CLOSE_FLAGS, ctx: vfs_context_current()); |
1234 | vnode_put(vp); |
1235 | return EINVAL; |
1236 | } |
1237 | mtx_lock(&audit_mtx); |
1238 | /* |
1239 | * XXXAUDIT: Should audit_suspended actually be cleared by |
1240 | * audit_worker? |
1241 | */ |
1242 | audit_suspended = 0; |
1243 | mtx_unlock(&audit_mtx); |
1244 | |
1245 | /* |
1246 | * The following gets unreferenced in audit_rotate_vnode() |
1247 | * after the rotation and it is no longer needed. |
1248 | */ |
1249 | cred = kauth_cred_get_with_ref(); |
1250 | audit_rotate_vnode(cred, vp); |
1251 | vnode_put(vp); |
1252 | |
1253 | return error; |
1254 | } |
1255 | |
1256 | #else /* !CONFIG_AUDIT */ |
1257 | |
1258 | int |
1259 | audit(proc_t p, struct audit_args *uap, int32_t *retval) |
1260 | { |
1261 | #pragma unused(p, uap, retval) |
1262 | |
1263 | return ENOSYS; |
1264 | } |
1265 | |
1266 | int |
1267 | auditon(proc_t p, struct auditon_args *uap, int32_t *retval) |
1268 | { |
1269 | #pragma unused(p, uap, retval) |
1270 | |
1271 | return ENOSYS; |
1272 | } |
1273 | |
1274 | int |
1275 | getauid(proc_t p, struct getauid_args *uap, int32_t *retval) |
1276 | { |
1277 | #pragma unused(p, uap, retval) |
1278 | |
1279 | return ENOSYS; |
1280 | } |
1281 | |
1282 | int |
1283 | setauid(proc_t p, struct setauid_args *uap, int32_t *retval) |
1284 | { |
1285 | #pragma unused(p, uap, retval) |
1286 | |
1287 | return ENOSYS; |
1288 | } |
1289 | |
1290 | int |
1291 | getaudit_addr(proc_t p, struct getaudit_addr_args *uap, int32_t *retval) |
1292 | { |
1293 | #pragma unused(p, uap, retval) |
1294 | |
1295 | return ENOSYS; |
1296 | } |
1297 | |
1298 | int |
1299 | setaudit_addr(proc_t p, struct setaudit_addr_args *uap, int32_t *retval) |
1300 | { |
1301 | #pragma unused(p, uap, retval) |
1302 | |
1303 | return ENOSYS; |
1304 | } |
1305 | |
1306 | int |
1307 | auditctl(proc_t p, struct auditctl_args *uap, int32_t *retval) |
1308 | { |
1309 | #pragma unused(p, uap, retval) |
1310 | |
1311 | return ENOSYS; |
1312 | } |
1313 | |
1314 | #endif /* CONFIG_AUDIT */ |
1315 | |