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