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 int 92 main(int argc, char *argv[]) 93 { 94 pid_t pid; /* process id of auditd read from auditdatafile */ 95 int sig = 0; /* signal to send auditd */ 96 char c; 97 char *first_option; 98 99 /* Internationalization */ 100 (void) setlocale(LC_ALL, ""); 101 (void) textdomain(TEXT_DOMAIN); 102 103 if (getuid() != 0) { 104 (void) fprintf(stderr, gettext("%s: not super-user\n"), 105 progname); 106 exit(2); 107 } 108 /* first option required */ 109 if ((c = getopt(argc, argv, "nstTv:")) == -1) { 110 (void) fprintf(stderr, gettext("usage: %s\n"), usage); 111 exit(3); 112 } 113 first_option = optarg; 114 /* second or more options not allowed; please pick one */ 115 if (getopt(argc, argv, "nstTv:") != -1) { 116 (void) fprintf(stderr, gettext("usage: %s\n"), usage); 117 exit(5); 118 } 119 switch (c) { 120 case 'n': 121 if (!is_valid_zone(1)) /* 1 == display error if any */ 122 exit(10); 123 124 sig = AU_SIG_NEXT_DIR; 125 break; 126 case 's': 127 if (!is_valid_zone(1)) /* 1 == display error if any */ 128 exit(10); 129 else if (!is_audit_control_ok(NULL)) 130 exit(7); 131 132 start_auditd(); 133 break; 134 case 't': 135 if (!is_valid_zone(0)) /* 0 == no error message display */ 136 exit(0); 137 /* use bmsunconv to permanently disable, -t for temporary */ 138 if (smf_disable_instance(instance_name, SMF_TEMPORARY) != 0) 139 display_smf_error(); 140 break; 141 case 'T': 142 if (!is_valid_zone(0)) /* 0 == no error message display */ 143 exit(0); 144 145 (void) smf_disable_instance(instance_name, SMF_TEMPORARY); 146 silent = 1; 147 break; 148 case 'v': 149 if (is_audit_control_ok(first_option)) { 150 (void) fprintf(stderr, gettext("syntax ok\n")); 151 exit(0); 152 } else { 153 exit(8); 154 } 155 break; 156 default: 157 (void) fprintf(stderr, gettext("usage: %s\n"), usage); 158 exit(6); 159 } 160 161 if (get_auditd_pid(&pid) != 0) { 162 if (silent) { 163 exit(0); 164 } else { 165 (void) fprintf(stderr, "%s: %s\n", progname, gettext( 166 "can't get process id of auditd from audit_data(4)")); 167 exit(4); 168 } 169 } 170 171 if ((sig != 0) && (kill(pid, sig) != 0)) { 172 if (silent) { 173 exit(0); 174 } else { 175 perror(progname); 176 (void) fprintf(stderr, 177 gettext("%s: cannot signal auditd\n"), progname); 178 exit(1); 179 } 180 } 181 return (0); 182 } 183 184 185 /* 186 * get_auditd_pid(&pid): 187 * 188 * reads PID from audit_data 189 * 190 * returns: 0 - successful 191 * 1 - error 192 */ 193 194 static int 195 get_auditd_pid(pid_t *p_pid) 196 { 197 FILE *adp; /* audit_data file pointer */ 198 int retstat; 199 200 if ((adp = fopen(auditdatafile, "r")) == NULL) { 201 if (!silent) 202 perror(progname); 203 return (1); 204 } 205 retstat = (fscanf(adp, "%ld", p_pid) != 1); 206 (void) fclose(adp); 207 return (retstat); 208 } 209 210 /* 211 * perform reasonableness check on audit_control or its standin; goal 212 * is that "audit -s" (1) not crash the system and (2) c2audit/auditd 213 * actually generates data. 214 * 215 * A NULL input is ok -- it is used to tell _openac() to use the 216 * real audit_control file, not a substitute. 217 */ 218 #define TRADITIONAL_MAX 1024 219 220 static boolean_t 221 is_audit_control_ok(char *filename) { 222 char buf[TRADITIONAL_MAX]; 223 int outputs = 0; 224 int state = 1; /* 1 is ok, 0 is not */ 225 int rc; 226 int min; 227 kva_t *kvlist; 228 char *value; 229 au_acinfo_t *ach; 230 231 ach = _openac(filename); /* open audit_control */ 232 if (ach == NULL) { 233 perror(progname); 234 exit(9); 235 } 236 /* 237 * There must be at least one directory or one plugin 238 * defined. 239 */ 240 if ((rc = _getacdir(ach, buf, TRADITIONAL_MAX)) == 0) { 241 outputs++; 242 } else if (rc < -1) { /* -1 is not found, others are errors */ 243 (void) fprintf(stderr, 244 gettext("%s: audit_control \"dir:\" spec invalid\n"), 245 progname); 246 state = 0; /* is_not_ok */ 247 } 248 249 /* 250 * _getacplug -- all that is of interest is the return code. 251 */ 252 _rewindac(ach); /* rewind audit_control */ 253 if ((rc = _getacplug(ach, &kvlist)) == 0) { 254 value = kva_match(kvlist, "name"); 255 if (value == NULL) { 256 (void) fprintf(stderr, gettext("%s: audit_control " 257 "\"plugin:\" missing name\n"), progname); 258 state = 0; /* is_not_ok */ 259 } 260 else 261 outputs++; 262 263 _kva_free(kvlist); 264 } else if (rc < -1) { 265 (void) fprintf(stderr, 266 gettext("%s: audit_control \"plugin:\" spec invalid\n"), 267 progname); 268 state = 0; /* is_not_ok */ 269 } 270 if (outputs == 0) { 271 (void) fprintf(stderr, 272 gettext("%s: audit_control must have either a " 273 "\"dir:\" or a \"plugin:\" specified.\n"), 274 progname); 275 state = 0; /* is_not_ok */ 276 } 277 /* minfree is not required */ 278 _rewindac(ach); 279 if ((rc = _getacmin(ach, &min)) < -1) { 280 (void) fprintf(stderr, 281 gettext( 282 "%s: audit_control \"minfree:\" spec invalid\n"), 283 progname); 284 state = 0; /* is_not_ok */ 285 } 286 /* flags is not required */ 287 _rewindac(ach); 288 if ((rc = _getacflg(ach, buf, TRADITIONAL_MAX)) < -1) { 289 (void) fprintf(stderr, 290 gettext("%s: audit_control \"flags:\" spec invalid\n"), 291 progname); 292 state = 0; /* is_not_ok */ 293 } 294 /* naflags is not required */ 295 _rewindac(ach); 296 if ((rc = _getacna(ach, buf, TRADITIONAL_MAX)) < -1) { 297 (void) fprintf(stderr, 298 gettext( 299 "%s: audit_control \"naflags:\" spec invalid\n"), 300 progname); 301 state = 0; /* is_not_ok */ 302 } 303 _endac(ach); 304 return (state); 305 } 306 307 /* 308 * The operations that call this function are only valid in the global 309 * zone unless the perzone audit policy is set. 310 * 311 * "!silent" and "show_err" are slightly different; silent is from 312 * -T for which no error messages should be displayed and show_err 313 * applies to more options (including -T) 314 * 315 */ 316 317 static boolean_t 318 is_valid_zone(boolean_t show_err) 319 { 320 long policy; 321 322 if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) { 323 if (!silent) 324 (void) fprintf(stderr, gettext( 325 "%s: Cannot read audit policy: %s\n"), 326 progname, strerror(errno)); 327 return (0); 328 } 329 if (policy & AUDIT_PERZONE) 330 return (1); 331 332 if (getzoneid() != GLOBAL_ZONEID) { 333 if (show_err) 334 (void) fprintf(stderr, 335 gettext("%s: Not valid in a local zone.\n"), 336 progname); 337 return (0); 338 } else { 339 return (1); 340 } 341 } 342 343 /* 344 * if auditd isn't running, start it. Otherwise refresh. 345 * First check to see if c2audit is loaded via the auditon() 346 * system call, then check SMF state. 347 */ 348 static void 349 start_auditd() 350 { 351 int audit_state; 352 char *state; 353 354 if (auditon(A_GETCOND, (caddr_t)&audit_state, 355 sizeof (audit_state)) != 0) 356 return; 357 358 if ((state = smf_get_state(instance_name)) == NULL) { 359 display_smf_error(); 360 return; 361 } 362 if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) { 363 if (smf_enable_instance(instance_name, 0) != 0) 364 display_smf_error(); 365 } else { 366 if (smf_refresh_instance(instance_name) != 0) 367 display_smf_error(); 368 } 369 free(state); 370 } 371 372 static void 373 display_smf_error() 374 { 375 int rc = scf_error(); 376 377 switch (rc) { 378 case SCF_ERROR_NOT_FOUND: 379 (void) fprintf(stderr, 380 "SMF error: \"%s\" not found.\n", 381 instance_name); 382 break; 383 default: 384 (void) fprintf(stderr, "SMF error %d\n", rc); 385 break; 386 } 387 } 388