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