1/*
2 * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
3 * Copyright (c) 2009 Christian S.J. Peron
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <config.h>
19
20#include <sys/types.h>
21
22#include <bsm/audit.h>
23#include <bsm/libbsm.h>
24#include <bsm/audit_uevents.h>
25
26#include <stdio.h>
27#include <string.h>
28#include <stdarg.h>
29#include <pwd.h>
30#include <errno.h>
31#include <unistd.h>
32
33#include "error.h"
34#include "bsm_audit.h"
35
36/*
37 * Solaris auditon() returns EINVAL if BSM audit not configured.
38 * OpenBSM returns ENOSYS for unimplemented options.
39 */
40#ifdef __sun
41# define AUDIT_NOT_CONFIGURED	EINVAL
42#else
43# define AUDIT_NOT_CONFIGURED	ENOSYS
44#endif
45
46static int
47audit_sudo_selected(int sf)
48{
49	auditinfo_addr_t ainfo_addr;
50	struct au_mask *mask;
51	auditinfo_t ainfo;
52	int rc, sorf;
53
54	if (getaudit_addr(&ainfo_addr, sizeof(ainfo_addr)) < 0) {
55		if (errno == ENOSYS) {
56			if (getaudit(&ainfo) < 0)
57				error(1, "getaudit: failed");
58			mask = &ainfo.ai_mask;
59		} else
60			error(1, "getaudit: failed");
61        } else
62		mask = &ainfo_addr.ai_mask;
63	sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE;
64	rc = au_preselect(AUE_sudo, mask, sorf, AU_PRS_REREAD);
65        return rc;
66}
67
68void
69bsm_audit_success(char **exec_args)
70{
71	auditinfo_addr_t ainfo_addr;
72	auditinfo_t ainfo;
73	token_t *tok;
74	au_id_t auid;
75	long au_cond;
76	int aufd;
77	pid_t pid;
78
79	pid = getpid();
80	/*
81	 * If we are not auditing, don't cut an audit record; just return.
82	 */
83	if (auditon(A_GETCOND, (caddr_t)&au_cond, sizeof(long)) < 0) {
84		if (errno == AUDIT_NOT_CONFIGURED)
85			return;
86		error(1, "Could not determine audit condition");
87	}
88	if (au_cond == AUC_NOAUDIT)
89		return;
90	/*
91	 * Check to see if the preselection masks are interested in seeing
92	 * this event.
93	 */
94	if (!audit_sudo_selected(0))
95		return;
96	if (getauid(&auid) < 0)
97		error(1, "getauid failed");
98	if ((aufd = au_open()) == -1)
99		error(1, "au_open: failed");
100	if (getaudit_addr(&ainfo_addr, sizeof(ainfo_addr)) == 0) {
101		tok = au_to_subject_ex(auid, geteuid(), getegid(), getuid(),
102		    getuid(), pid, pid, &ainfo_addr.ai_termid);
103	} else if (errno == ENOSYS) {
104		/*
105		 * NB: We should probably watch out for ERANGE here.
106		 */
107		if (getaudit(&ainfo) < 0)
108			error(1, "getaudit: failed");
109		tok = au_to_subject(auid, geteuid(), getegid(), getuid(),
110		    getuid(), pid, pid, &ainfo.ai_termid);
111	} else
112		error(1, "getaudit: failed");
113	if (tok == NULL)
114		error(1, "au_to_subject: failed");
115	au_write(aufd, tok);
116	tok = au_to_exec_args(exec_args);
117	if (tok == NULL)
118		error(1, "au_to_exec_args: failed");
119	au_write(aufd, tok);
120	tok = au_to_return32(0, 0);
121	if (tok == NULL)
122		error(1, "au_to_return32: failed");
123	au_write(aufd, tok);
124	if (au_close(aufd, 1, AUE_sudo) == -1)
125		error(1, "unable to commit audit record");
126}
127
128void
129bsm_audit_failure(char **exec_args, char const *const fmt, va_list ap)
130{
131	auditinfo_addr_t ainfo_addr;
132	auditinfo_t ainfo;
133	char text[256];
134	token_t *tok;
135	long au_cond;
136	au_id_t auid;
137	pid_t pid;
138	int aufd;
139
140	pid = getpid();
141	/*
142	 * If we are not auditing, don't cut an audit record; just return.
143	 */
144	if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
145		if (errno == AUDIT_NOT_CONFIGURED)
146			return;
147		error(1, "Could not determine audit condition");
148	}
149	if (au_cond == AUC_NOAUDIT)
150		return;
151	if (!audit_sudo_selected(1))
152		return;
153	if (getauid(&auid) < 0)
154		error(1, "getauid: failed");
155	if ((aufd = au_open()) == -1)
156		error(1, "au_open: failed");
157	if (getaudit_addr(&ainfo_addr, sizeof(ainfo_addr)) == 0) {
158		tok = au_to_subject_ex(auid, geteuid(), getegid(), getuid(),
159		    getuid(), pid, pid, &ainfo_addr.ai_termid);
160	} else if (errno == ENOSYS) {
161		if (getaudit(&ainfo) < 0)
162			error(1, "getaudit: failed");
163		tok = au_to_subject(auid, geteuid(), getegid(), getuid(),
164		    getuid(), pid, pid, &ainfo.ai_termid);
165	} else
166		error(1, "getaudit: failed");
167	if (tok == NULL)
168		error(1, "au_to_subject: failed");
169	au_write(aufd, tok);
170	if (exec_args != NULL) {
171		tok = au_to_exec_args(exec_args);
172		if (tok == NULL)
173			log_error(0, "au_to_exec_args: failed");
174		au_write(aufd, tok);
175	}
176	tok = au_to_exec_args(exec_args);
177	if (tok == NULL)
178		error(1, "au_to_exec_args: failed");
179	au_write(aufd, tok);
180	(void) vsnprintf(text, sizeof(text), fmt, ap);
181	tok = au_to_text(text);
182	if (tok == NULL)
183		error(1, "au_to_text: failed");
184	au_write(aufd, tok);
185	tok = au_to_return32(EPERM, 1);
186	if (tok == NULL)
187		error(1, "au_to_return32: failed");
188	au_write(aufd, tok);
189	if (au_close(aufd, 1, AUE_sudo) == -1)
190		error(1, "unable to commit audit record");
191}
192