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