audit.c (cee38678) audit.c (1a578a15)
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25#pragma ident "%Z%%M% %I% %E% SMI"
26
27#include <fcntl.h>
28#include <libscf.h>
29#include <secdb.h>
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <sys/file.h>
34#include <sys/types.h>
35#include <sys/wait.h>
36#include <signal.h>
37#include <sys/param.h>
38#include <unistd.h>
39#include <bsm/audit.h>
40#include <bsm/libbsm.h>
41#include <locale.h>
42#include <audit_sig_infc.h>
43#include <zone.h>
44
45#if !defined(TEXT_DOMAIN)
46#define TEXT_DOMAIN "SUNW_OST_OSCMD"
47#endif
48
49#define VERIFY -1
50
51/* GLOBALS */
52static char *auditdatafile = AUDITDATAFILE;
53static char *progname = "audit";
54static char *usage = "audit [-n] | [-s] | [-t] | [-v filepath]";
55static int silent = 0;
56static char *instance_name = "svc:/system/auditd:default";
57
58static int get_auditd_pid();
59static void display_smf_error();
60
61static boolean_t is_audit_control_ok(char *); /* file validation */
62static boolean_t is_valid_zone(boolean_t); /* operation ok in this zone? */
63static int start_auditd(); /* start audit daemon */
64
65/*
66 * audit() - This program serves as a general administrator's interface to
67 * the audit trail. Only one option is valid at a time.
68 *
69 * input:
70 * audit -s
71 * - signal audit daemon to read audit_control file and
72 * start auditd if needed.
73 * audit -n
74 * - signal audit daemon to use next audit_control audit directory.
75 * audit -t
76 * - signal audit daemon to disable auditing.
77 * audit -T
78 * - signal audit daemon to disable auditing report no errors.
79 * audit -v filepath
80 * - validate audit_control parameters but use filepath for
81 * the name. Emit errors or "syntax ok"
82 *
83 *
84 * output:
85 *
86 * returns: 0 - command successful
87 * >0 - command failed
88 */
89
90int
91main(int argc, char *argv[])
92{
93 pid_t pid; /* process id of auditd read from auditdatafile */
94 int sig = 0; /* signal to send auditd */
95 char c;
96 char *first_option;
97
98 /* Internationalization */
99 (void) setlocale(LC_ALL, "");
100 (void) textdomain(TEXT_DOMAIN);
101
102 /* first option required */
103 if ((c = getopt(argc, argv, "nstTv:")) == -1) {
104 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
105 exit(3);
106 }
107 first_option = optarg;
108 /* second or more options not allowed; please pick one */
109 if (getopt(argc, argv, "nstTv:") != -1) {
110 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
111 exit(5);
112 }
113 switch (c) {
114 case 'n':
115 if (!is_valid_zone(1)) /* 1 == display error if any */
116 exit(10);
117
118 sig = AU_SIG_NEXT_DIR;
119 break;
120 case 's':
121 if (!is_valid_zone(1)) /* 1 == display error if any */
122 exit(10);
123 else if (!is_audit_control_ok(NULL))
124 exit(7);
125
126 return (start_auditd());
127 case 't':
128 if (!is_valid_zone(0)) /* 0 == no error message display */
129 exit(10);
130 /* use bmsunconv to permanently disable, -t for temporary */
131 if (smf_disable_instance(instance_name, SMF_TEMPORARY) != 0) {
132 display_smf_error();
133 exit(11);
134 }
135 break;
136 case 'T':
137 silent = 1;
138 if (!is_valid_zone(0)) /* 0 == no error message display */
139 exit(10);
140
141 if (smf_disable_instance(instance_name, SMF_TEMPORARY) != 0) {
142 exit(11);
143 }
144 break;
145 case 'v':
146 if (is_audit_control_ok(first_option)) {
147 (void) fprintf(stderr, gettext("syntax ok\n"));
148 exit(0);
149 } else {
150 exit(8);
151 }
152 break;
153 default:
154 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
155 exit(6);
156 }
157
158 if (sig != 0) {
159 if (get_auditd_pid(&pid) != 0) {
160 (void) fprintf(stderr, "%s: %s\n", progname,
161 gettext("can't get process id of auditd from "
162 "audit_data(4)"));
163 exit(4);
164 }
165
166 if (kill(pid, sig) != 0) {
167 perror(progname);
168 (void) fprintf(stderr,
169 gettext("%s: cannot signal auditd\n"), progname);
170 exit(1);
171 }
172 }
173 return (0);
174}
175
176
177/*
178 * get_auditd_pid(&pid):
179 *
180 * reads PID from audit_data
181 *
182 * returns: 0 - successful
183 * 1 - error
184 */
185
186static int
187get_auditd_pid(pid_t *p_pid)
188{
189 FILE *adp; /* audit_data file pointer */
190 int retstat;
191
192 if ((adp = fopen(auditdatafile, "r")) == NULL) {
193 if (!silent)
194 perror(progname);
195 return (1);
196 }
197 retstat = (fscanf(adp, "%ld", p_pid) != 1);
198 (void) fclose(adp);
199 return (retstat);
200}
201
202/*
203 * perform reasonableness check on audit_control or its standin; goal
204 * is that "audit -s" (1) not crash the system and (2) c2audit/auditd
205 * actually generates data.
206 *
207 * A NULL input is ok -- it is used to tell _openac() to use the
208 * real audit_control file, not a substitute.
209 */
210#define TRADITIONAL_MAX 1024
211
212static boolean_t
213is_audit_control_ok(char *filename) {
214 char buf[TRADITIONAL_MAX];
215 int outputs = 0;
216 int state = 1; /* 1 is ok, 0 is not */
217 int rc;
218 int min;
219 kva_t *kvlist;
23 * Use is subject to license terms.
24 */
25#pragma ident "%Z%%M% %I% %E% SMI"
26
27#include <fcntl.h>
28#include <libscf.h>
29#include <secdb.h>
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <sys/file.h>
34#include <sys/types.h>
35#include <sys/wait.h>
36#include <signal.h>
37#include <sys/param.h>
38#include <unistd.h>
39#include <bsm/audit.h>
40#include <bsm/libbsm.h>
41#include <locale.h>
42#include <audit_sig_infc.h>
43#include <zone.h>
44
45#if !defined(TEXT_DOMAIN)
46#define TEXT_DOMAIN "SUNW_OST_OSCMD"
47#endif
48
49#define VERIFY -1
50
51/* GLOBALS */
52static char *auditdatafile = AUDITDATAFILE;
53static char *progname = "audit";
54static char *usage = "audit [-n] | [-s] | [-t] | [-v filepath]";
55static int silent = 0;
56static char *instance_name = "svc:/system/auditd:default";
57
58static int get_auditd_pid();
59static void display_smf_error();
60
61static boolean_t is_audit_control_ok(char *); /* file validation */
62static boolean_t is_valid_zone(boolean_t); /* operation ok in this zone? */
63static int start_auditd(); /* start audit daemon */
64
65/*
66 * audit() - This program serves as a general administrator's interface to
67 * the audit trail. Only one option is valid at a time.
68 *
69 * input:
70 * audit -s
71 * - signal audit daemon to read audit_control file and
72 * start auditd if needed.
73 * audit -n
74 * - signal audit daemon to use next audit_control audit directory.
75 * audit -t
76 * - signal audit daemon to disable auditing.
77 * audit -T
78 * - signal audit daemon to disable auditing report no errors.
79 * audit -v filepath
80 * - validate audit_control parameters but use filepath for
81 * the name. Emit errors or "syntax ok"
82 *
83 *
84 * output:
85 *
86 * returns: 0 - command successful
87 * >0 - command failed
88 */
89
90int
91main(int argc, char *argv[])
92{
93 pid_t pid; /* process id of auditd read from auditdatafile */
94 int sig = 0; /* signal to send auditd */
95 char c;
96 char *first_option;
97
98 /* Internationalization */
99 (void) setlocale(LC_ALL, "");
100 (void) textdomain(TEXT_DOMAIN);
101
102 /* first option required */
103 if ((c = getopt(argc, argv, "nstTv:")) == -1) {
104 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
105 exit(3);
106 }
107 first_option = optarg;
108 /* second or more options not allowed; please pick one */
109 if (getopt(argc, argv, "nstTv:") != -1) {
110 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
111 exit(5);
112 }
113 switch (c) {
114 case 'n':
115 if (!is_valid_zone(1)) /* 1 == display error if any */
116 exit(10);
117
118 sig = AU_SIG_NEXT_DIR;
119 break;
120 case 's':
121 if (!is_valid_zone(1)) /* 1 == display error if any */
122 exit(10);
123 else if (!is_audit_control_ok(NULL))
124 exit(7);
125
126 return (start_auditd());
127 case 't':
128 if (!is_valid_zone(0)) /* 0 == no error message display */
129 exit(10);
130 /* use bmsunconv to permanently disable, -t for temporary */
131 if (smf_disable_instance(instance_name, SMF_TEMPORARY) != 0) {
132 display_smf_error();
133 exit(11);
134 }
135 break;
136 case 'T':
137 silent = 1;
138 if (!is_valid_zone(0)) /* 0 == no error message display */
139 exit(10);
140
141 if (smf_disable_instance(instance_name, SMF_TEMPORARY) != 0) {
142 exit(11);
143 }
144 break;
145 case 'v':
146 if (is_audit_control_ok(first_option)) {
147 (void) fprintf(stderr, gettext("syntax ok\n"));
148 exit(0);
149 } else {
150 exit(8);
151 }
152 break;
153 default:
154 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
155 exit(6);
156 }
157
158 if (sig != 0) {
159 if (get_auditd_pid(&pid) != 0) {
160 (void) fprintf(stderr, "%s: %s\n", progname,
161 gettext("can't get process id of auditd from "
162 "audit_data(4)"));
163 exit(4);
164 }
165
166 if (kill(pid, sig) != 0) {
167 perror(progname);
168 (void) fprintf(stderr,
169 gettext("%s: cannot signal auditd\n"), progname);
170 exit(1);
171 }
172 }
173 return (0);
174}
175
176
177/*
178 * get_auditd_pid(&pid):
179 *
180 * reads PID from audit_data
181 *
182 * returns: 0 - successful
183 * 1 - error
184 */
185
186static int
187get_auditd_pid(pid_t *p_pid)
188{
189 FILE *adp; /* audit_data file pointer */
190 int retstat;
191
192 if ((adp = fopen(auditdatafile, "r")) == NULL) {
193 if (!silent)
194 perror(progname);
195 return (1);
196 }
197 retstat = (fscanf(adp, "%ld", p_pid) != 1);
198 (void) fclose(adp);
199 return (retstat);
200}
201
202/*
203 * perform reasonableness check on audit_control or its standin; goal
204 * is that "audit -s" (1) not crash the system and (2) c2audit/auditd
205 * actually generates data.
206 *
207 * A NULL input is ok -- it is used to tell _openac() to use the
208 * real audit_control file, not a substitute.
209 */
210#define TRADITIONAL_MAX 1024
211
212static boolean_t
213is_audit_control_ok(char *filename) {
214 char buf[TRADITIONAL_MAX];
215 int outputs = 0;
216 int state = 1; /* 1 is ok, 0 is not */
217 int rc;
218 int min;
219 kva_t *kvlist;
220 char *value;
220 char *plugin_name;
221 char *plugin_dir;
221 au_acinfo_t *ach;
222
223 ach = _openac(filename); /* open audit_control */
224 if (ach == NULL) {
225 perror(progname);
226 exit(9);
227 }
228 /*
229 * There must be at least one directory or one plugin
230 * defined.
231 */
232 if ((rc = _getacdir(ach, buf, TRADITIONAL_MAX)) == 0) {
233 outputs++;
234 } else if (rc < -1) { /* -1 is not found, others are errors */
235 (void) fprintf(stderr,
236 gettext("%s: audit_control \"dir:\" spec invalid\n"),
237 progname);
238 state = 0; /* is_not_ok */
239 }
240
241 /*
242 * _getacplug -- all that is of interest is the return code.
243 */
244 _rewindac(ach); /* rewind audit_control */
222 au_acinfo_t *ach;
223
224 ach = _openac(filename); /* open audit_control */
225 if (ach == NULL) {
226 perror(progname);
227 exit(9);
228 }
229 /*
230 * There must be at least one directory or one plugin
231 * defined.
232 */
233 if ((rc = _getacdir(ach, buf, TRADITIONAL_MAX)) == 0) {
234 outputs++;
235 } else if (rc < -1) { /* -1 is not found, others are errors */
236 (void) fprintf(stderr,
237 gettext("%s: audit_control \"dir:\" spec invalid\n"),
238 progname);
239 state = 0; /* is_not_ok */
240 }
241
242 /*
243 * _getacplug -- all that is of interest is the return code.
244 */
245 _rewindac(ach); /* rewind audit_control */
245 if ((rc = _getacplug(ach, &kvlist)) == 0) {
246 value = kva_match(kvlist, "name");
247 if (value == NULL) {
246 while ((rc = _getacplug(ach, &kvlist)) == 0) {
247 plugin_name = kva_match(kvlist, "name");
248 if (plugin_name == NULL) {
248 (void) fprintf(stderr, gettext("%s: audit_control "
249 "\"plugin:\" missing name\n"), progname);
250 state = 0; /* is_not_ok */
249 (void) fprintf(stderr, gettext("%s: audit_control "
250 "\"plugin:\" missing name\n"), progname);
251 state = 0; /* is_not_ok */
252 } else {
253 if (strcmp(plugin_name, "audit_binfile.so") == 0) {
254 plugin_dir = kva_match(kvlist, "p_dir");
255 if ((plugin_dir == NULL) && (outputs == 0)) {
256 (void) fprintf(stderr,
257 gettext("%s: audit_control "
258 "\"plugin:\" missing p_dir\n"),
259 progname);
260 state = 0; /* is_not_ok */
261 } else {
262 outputs++;
263 }
264 }
251 }
265 }
252 else
253 outputs++;
254
255 _kva_free(kvlist);
266 _kva_free(kvlist);
256 } else if (rc < -1) {
267 }
268 if (rc < -1) {
257 (void) fprintf(stderr,
258 gettext("%s: audit_control \"plugin:\" spec invalid\n"),
259 progname);
260 state = 0; /* is_not_ok */
261 }
262 if (outputs == 0) {
263 (void) fprintf(stderr,
264 gettext("%s: audit_control must have either a "
269 (void) fprintf(stderr,
270 gettext("%s: audit_control \"plugin:\" spec invalid\n"),
271 progname);
272 state = 0; /* is_not_ok */
273 }
274 if (outputs == 0) {
275 (void) fprintf(stderr,
276 gettext("%s: audit_control must have either a "
265 "\"dir:\" or a \"plugin:\" specified.\n"),
277 "valid \"dir:\" entry or a valid \"plugin:\" "
278 "entry with \"p_dir:\" specified.\n"),
266 progname);
267 state = 0; /* is_not_ok */
268 }
269 /* minfree is not required */
270 _rewindac(ach);
271 if ((rc = _getacmin(ach, &min)) < -1) {
272 (void) fprintf(stderr,
273 gettext(
274 "%s: audit_control \"minfree:\" spec invalid\n"),
275 progname);
276 state = 0; /* is_not_ok */
277 }
278 /* flags is not required */
279 _rewindac(ach);
280 if ((rc = _getacflg(ach, buf, TRADITIONAL_MAX)) < -1) {
281 (void) fprintf(stderr,
282 gettext("%s: audit_control \"flags:\" spec invalid\n"),
283 progname);
284 state = 0; /* is_not_ok */
285 }
286 /* naflags is not required */
287 _rewindac(ach);
288 if ((rc = _getacna(ach, buf, TRADITIONAL_MAX)) < -1) {
289 (void) fprintf(stderr,
290 gettext(
291 "%s: audit_control \"naflags:\" spec invalid\n"),
292 progname);
293 state = 0; /* is_not_ok */
294 }
295 _endac(ach);
296 return (state);
297}
298
299/*
300 * The operations that call this function are only valid in the global
301 * zone unless the perzone audit policy is set.
302 *
303 * "!silent" and "show_err" are slightly different; silent is from
304 * -T for which no error messages should be displayed and show_err
305 * applies to more options (including -T)
306 *
307 */
308
309static boolean_t
310is_valid_zone(boolean_t show_err)
311{
312 long policy;
313
314 if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) {
315 if (!silent)
316 (void) fprintf(stderr, gettext(
317 "%s: Cannot read audit policy: %s\n"),
318 progname, strerror(errno));
319 return (0);
320 }
321 if (policy & AUDIT_PERZONE)
322 return (1);
323
324 if (getzoneid() != GLOBAL_ZONEID) {
325 if (show_err)
326 (void) fprintf(stderr,
327 gettext("%s: Not valid in a local zone.\n"),
328 progname);
329 return (0);
330 } else {
331 return (1);
332 }
333}
334
335/*
336 * if auditd isn't running, start it. Otherwise refresh.
337 * First check to see if c2audit is loaded via the auditon()
338 * system call, then check SMF state.
339 */
340static int
341start_auditd()
342{
343 int audit_state;
344 char *state;
345
346 if (auditon(A_GETCOND, (caddr_t)&audit_state,
347 sizeof (audit_state)) != 0)
348 return (12);
349
350 if ((state = smf_get_state(instance_name)) == NULL) {
351 display_smf_error();
352 return (13);
353 }
354 if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) {
355 if (smf_enable_instance(instance_name, 0) != 0) {
356 display_smf_error();
357 free(state);
358 return (14);
359 }
360 } else {
361 if (smf_refresh_instance(instance_name) != 0) {
362 display_smf_error();
363 free(state);
364 return (15);
365 }
366 }
367 free(state);
368 return (0);
369}
370
371static void
372display_smf_error()
373{
374 int rc = scf_error();
375
376 switch (rc) {
377 case SCF_ERROR_NOT_FOUND:
378 (void) fprintf(stderr,
379 "SMF error: \"%s\" not found.\n",
380 instance_name);
381 break;
382 default:
383 (void) fprintf(stderr, "SMF error: %s\n", scf_strerror(rc));
384 break;
385 }
386}
279 progname);
280 state = 0; /* is_not_ok */
281 }
282 /* minfree is not required */
283 _rewindac(ach);
284 if ((rc = _getacmin(ach, &min)) < -1) {
285 (void) fprintf(stderr,
286 gettext(
287 "%s: audit_control \"minfree:\" spec invalid\n"),
288 progname);
289 state = 0; /* is_not_ok */
290 }
291 /* flags is not required */
292 _rewindac(ach);
293 if ((rc = _getacflg(ach, buf, TRADITIONAL_MAX)) < -1) {
294 (void) fprintf(stderr,
295 gettext("%s: audit_control \"flags:\" spec invalid\n"),
296 progname);
297 state = 0; /* is_not_ok */
298 }
299 /* naflags is not required */
300 _rewindac(ach);
301 if ((rc = _getacna(ach, buf, TRADITIONAL_MAX)) < -1) {
302 (void) fprintf(stderr,
303 gettext(
304 "%s: audit_control \"naflags:\" spec invalid\n"),
305 progname);
306 state = 0; /* is_not_ok */
307 }
308 _endac(ach);
309 return (state);
310}
311
312/*
313 * The operations that call this function are only valid in the global
314 * zone unless the perzone audit policy is set.
315 *
316 * "!silent" and "show_err" are slightly different; silent is from
317 * -T for which no error messages should be displayed and show_err
318 * applies to more options (including -T)
319 *
320 */
321
322static boolean_t
323is_valid_zone(boolean_t show_err)
324{
325 long policy;
326
327 if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) {
328 if (!silent)
329 (void) fprintf(stderr, gettext(
330 "%s: Cannot read audit policy: %s\n"),
331 progname, strerror(errno));
332 return (0);
333 }
334 if (policy & AUDIT_PERZONE)
335 return (1);
336
337 if (getzoneid() != GLOBAL_ZONEID) {
338 if (show_err)
339 (void) fprintf(stderr,
340 gettext("%s: Not valid in a local zone.\n"),
341 progname);
342 return (0);
343 } else {
344 return (1);
345 }
346}
347
348/*
349 * if auditd isn't running, start it. Otherwise refresh.
350 * First check to see if c2audit is loaded via the auditon()
351 * system call, then check SMF state.
352 */
353static int
354start_auditd()
355{
356 int audit_state;
357 char *state;
358
359 if (auditon(A_GETCOND, (caddr_t)&audit_state,
360 sizeof (audit_state)) != 0)
361 return (12);
362
363 if ((state = smf_get_state(instance_name)) == NULL) {
364 display_smf_error();
365 return (13);
366 }
367 if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) {
368 if (smf_enable_instance(instance_name, 0) != 0) {
369 display_smf_error();
370 free(state);
371 return (14);
372 }
373 } else {
374 if (smf_refresh_instance(instance_name) != 0) {
375 display_smf_error();
376 free(state);
377 return (15);
378 }
379 }
380 free(state);
381 return (0);
382}
383
384static void
385display_smf_error()
386{
387 int rc = scf_error();
388
389 switch (rc) {
390 case SCF_ERROR_NOT_FOUND:
391 (void) fprintf(stderr,
392 "SMF error: \"%s\" not found.\n",
393 instance_name);
394 break;
395 default:
396 (void) fprintf(stderr, "SMF error: %s\n", scf_strerror(rc));
397 break;
398 }
399}