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 /*
  23  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  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/stat.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 <zone.h>
  43 #include <audit_scf.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     *progname = "audit";
  53 static char     *usage = "audit [-n] | [-s] | [-t] | [-v]";
  54 static int      silent = 0;
  55 
  56 static void     display_smf_error();
  57 
  58 static boolean_t is_audit_config_ok();          /* config validation  */
  59 static boolean_t is_valid_zone(boolean_t);      /* operation ok in this zone? */
  60 static boolean_t contains_valid_dirs(char *);   /* p_dir contents validation */
  61 static boolean_t validate_path(char *);         /* is it path to dir? */
  62 static void     start_auditd();                 /* start audit daemon */
  63 static int      sig_auditd(int);                /* send signal to auditd */
  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 configuration and
  72  *                start auditd if needed.
  73  *      audit -n
  74  *              - signal audit daemon to use next audit_binfile directory.
  75  *      audit -t
  76  *              - signal audit daemon to disable auditing.
  77  *      audit -T
  78  *              - signal audit daemon to temporarily disable auditing reporting
  79  *                no errors.
  80  *      audit -v
  81  *              - validate audit configuration parameters;
  82  *                Print errors or "configuration 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         int     c;
  95 
  96         /* Internationalization */
  97         (void) setlocale(LC_ALL, "");
  98         (void) textdomain(TEXT_DOMAIN);
  99 
 100         /* second or more options not allowed; please pick one */
 101         if (argc > 2) {
 102                 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
 103                 exit(1);
 104         }
 105 
 106         /* first option required */
 107         if ((c = getopt(argc, argv, "nstTv")) == -1) {
 108                 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
 109                 exit(1);
 110         }
 111 
 112         switch (c) {
 113         case 'n':
 114                 if (!is_valid_zone(1))  /* 1 == display error if any */
 115                         exit(1);
 116 
 117                 if (sig_auditd(SIGUSR1) != 0)
 118                         exit(1);
 119                 break;
 120         case 's':
 121                 if (!is_valid_zone(1))  /* 1 == display error if any */
 122                         exit(1);
 123                 else if (!is_audit_config_ok())
 124                         exit(1);
 125 
 126                 start_auditd();
 127                 return (0);
 128         case 't':
 129                 if (!is_valid_zone(0))  /* 0 == no error message display */
 130                         exit(1);
 131                 if (smf_disable_instance(AUDITD_FMRI, 0) != 0) {
 132                         display_smf_error();
 133                         exit(1);
 134                 }
 135                 break;
 136         case 'T':
 137                 silent = 1;
 138                 if (!is_valid_zone(0))  /* 0 == no error message display */
 139                         exit(1);
 140                 if (smf_disable_instance(AUDITD_FMRI, SMF_TEMPORARY) != 0) {
 141                         exit(1);
 142                 }
 143                 break;
 144         case 'v':
 145                 if (is_audit_config_ok()) {
 146                         (void) fprintf(stderr, gettext("configuration ok\n"));
 147                         exit(0);
 148                 } else {
 149                         exit(1);
 150                 }
 151                 break;
 152         default:
 153                 (void) fprintf(stderr, gettext("usage: %s\n"), usage);
 154                 exit(1);
 155         }
 156 
 157         return (0);
 158 }
 159 
 160 /*
 161  * sig_auditd(sig)
 162  *
 163  * send a signal to auditd service
 164  *
 165  * returns:     0 - successful
 166  *              1 - error
 167  */
 168 
 169 static int
 170 sig_auditd(int sig)
 171 {
 172         scf_simple_prop_t *prop = NULL;
 173         uint64_t        *cid = NULL;
 174 
 175         if ((prop = scf_simple_prop_get(NULL, AUDITD_FMRI, SCF_PG_RESTARTER,
 176             SCF_PROPERTY_CONTRACT)) == NULL) {
 177                 display_smf_error();
 178                 return (1);
 179         }
 180         if ((scf_simple_prop_numvalues(prop) < 0) ||
 181             (cid = scf_simple_prop_next_count(prop)) == NULL) {
 182                 scf_simple_prop_free(prop);
 183                 display_smf_error();
 184                 return (1);
 185         }
 186         if (sigsend(P_CTID, (ctid_t)*cid, sig) != 0) {
 187                 perror("audit: can't signal auditd");
 188                 scf_simple_prop_free(prop);
 189                 return (1);
 190         }
 191         scf_simple_prop_free(prop);
 192         return (0);
 193 }
 194 
 195 /*
 196  * perform reasonableness check on audit configuration
 197  */
 198 
 199 static boolean_t
 200 is_audit_config_ok() {
 201         int                     state = B_TRUE; /* B_TRUE/B_FALSE = ok/not_ok */
 202         char                    *cval_str;
 203         int                     cval_int;
 204         kva_t                   *kvlist;
 205         scf_plugin_kva_node_t   *plugin_kva_ll;
 206         scf_plugin_kva_node_t   *plugin_kva_ll_head;
 207         boolean_t               one_plugin_enabled = B_FALSE;
 208 
 209         /*
 210          * There must be at least one active plugin configured; if the
 211          * configured plugin is audit_binfile(5), then the p_dir must not be
 212          * empty.
 213          */
 214         if (!do_getpluginconfig_scf(NULL, &plugin_kva_ll)) {
 215                 (void) fprintf(stderr,
 216                     gettext("Could not get plugin configuration.\n"));
 217                 exit(1);
 218         }
 219 
 220         plugin_kva_ll_head = plugin_kva_ll;
 221 
 222         while (plugin_kva_ll != NULL) {
 223                 kvlist = plugin_kva_ll->plugin_kva;
 224 
 225                 if (!one_plugin_enabled) {
 226                         cval_str = kva_match(kvlist, "active");
 227                         if (atoi(cval_str) == 1) {
 228                                 one_plugin_enabled = B_TRUE;
 229                         }
 230                 }
 231 
 232                 if (strcmp((char *)&(*plugin_kva_ll).plugin_name,
 233                     "audit_binfile") == 0) {
 234                         cval_str = kva_match(kvlist, "p_dir");
 235                         if (*cval_str == '\0' || cval_str == NULL) {
 236                                 (void) fprintf(stderr,
 237                                     gettext("%s: audit_binfile(5) \"p_dir:\" "
 238                                     "attribute empty\n"), progname);
 239                                 state = B_FALSE;
 240                         } else if (!contains_valid_dirs(cval_str)) {
 241                                 (void) fprintf(stderr,
 242                                     gettext("%s: audit_binfile(5) \"p_dir:\" "
 243                                     "attribute invalid\n"), progname);
 244                                 state = B_FALSE;
 245                         }
 246 
 247                         cval_str = kva_match(kvlist, "p_minfree");
 248                         cval_int = atoi(cval_str);
 249                         if (cval_int < 0 || cval_int > 100) {
 250                                 (void) fprintf(stderr,
 251                                     gettext("%s: audit_binfile(5) "
 252                                     "\"p_minfree:\" attribute invalid\n"),
 253                                     progname);
 254                                 state = B_FALSE;
 255                         }
 256                 }
 257 
 258                 plugin_kva_ll = plugin_kva_ll->next;
 259         }
 260 
 261         plugin_kva_ll_free(plugin_kva_ll_head);
 262 
 263         if (!one_plugin_enabled) {
 264                 (void) fprintf(stderr, gettext("%s: no active plugin found\n"),
 265                     progname);
 266                 state = B_FALSE;
 267         }
 268 
 269         return (state);
 270 }
 271 
 272 /*
 273  * The operations that call this function are only valid in the global
 274  * zone unless the perzone audit policy is set.
 275  *
 276  * "!silent" and "show_err" are slightly different; silent is from
 277  * -T for which no error messages should be displayed and show_err
 278  * applies to more options (including -T)
 279  *
 280  */
 281 
 282 static boolean_t
 283 is_valid_zone(boolean_t show_err)
 284 {
 285         uint32_t        policy;
 286 
 287         if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) {
 288                 if (!silent) {
 289                         (void) fprintf(stderr, gettext(
 290                             "%s: Cannot read audit policy:  %s\n"),
 291                             progname, strerror(errno));
 292                 }
 293                 return (0);
 294         }
 295         if (policy & AUDIT_PERZONE)
 296                 return (1);
 297 
 298         if (getzoneid() != GLOBAL_ZONEID) {
 299                 if (show_err)
 300                         (void) fprintf(stderr,
 301                             gettext("%s: Not valid in a local zone.\n"),
 302                             progname);
 303                 return (0);
 304         } else {
 305                 return (1);
 306         }
 307 }
 308 
 309 /*
 310  * Verify, whether the dirs_str contains at least one currently valid path to
 311  * the directory. All invalid paths are reported. In case no valid directory
 312  * path is found function returns B_FALSE, otherwise B_TRUE.
 313  */
 314 
 315 static boolean_t
 316 contains_valid_dirs(char *dirs_str)
 317 {
 318         boolean_t       rc = B_FALSE;
 319         boolean_t       rc_validate_path = B_TRUE;
 320         char            *tok_ptr;
 321         char            *tok_lasts;
 322 
 323         if (dirs_str == NULL) {
 324                 return (rc);
 325         }
 326 
 327         if ((tok_ptr = strtok_r(dirs_str, ",", &tok_lasts)) != NULL) {
 328                 if (validate_path(tok_ptr)) {
 329                         rc = B_TRUE;
 330                 } else {
 331                         rc_validate_path = B_FALSE;
 332                 }
 333                 while ((tok_ptr = strtok_r(NULL, ",", &tok_lasts)) != NULL) {
 334                         if (validate_path(tok_ptr)) {
 335                                 rc = B_TRUE;
 336                         } else {
 337                                 rc_validate_path = B_FALSE;
 338                         }
 339                 }
 340         }
 341 
 342         if (rc && !rc_validate_path) {
 343                 (void) fprintf(stderr, gettext("%s: at least one valid "
 344                     "directory path found\n"), progname);
 345         }
 346 
 347         return (rc);
 348 }
 349 
 350 /*
 351  * Verify, that the dir_path is path to a directory.
 352  */
 353 
 354 static boolean_t
 355 validate_path(char *dir_path)
 356 {
 357         boolean_t       rc = B_FALSE;
 358         struct stat     statbuf;
 359 
 360         if (dir_path == NULL) {
 361                 return (rc);
 362         }
 363 
 364         if (stat(dir_path, &statbuf) == -1) {
 365                 (void) fprintf(stderr, gettext("%s: %s error: %s\n"), progname,
 366                     dir_path, strerror(errno));
 367         } else if (statbuf.st_mode & S_IFDIR) {
 368                         rc = B_TRUE;
 369         } else {
 370                 (void) fprintf(stderr, gettext("%s: %s is not a directory\n"),
 371                     progname, dir_path);
 372         }
 373 
 374         return (rc);
 375 }
 376 
 377 /*
 378  * if auditd isn't running, start it.  Otherwise refresh.
 379  * First check to see if c2audit is loaded via the auditon()
 380  * system call, then check SMF state.
 381  */
 382 static void
 383 start_auditd()
 384 {
 385         int     audit_state;
 386         char    *state;
 387 
 388         if (auditon(A_GETCOND, (caddr_t)&audit_state,
 389             sizeof (audit_state)) != 0)
 390                 exit(1);
 391 
 392         if ((state = smf_get_state(AUDITD_FMRI)) == NULL) {
 393                 display_smf_error();
 394                 exit(1);
 395         }
 396         if (strcmp(SCF_STATE_STRING_ONLINE, state) != 0) {
 397                 if (smf_enable_instance(AUDITD_FMRI, 0) != 0) {
 398                         display_smf_error();
 399                         free(state);
 400                         exit(1);
 401                 }
 402         } else {
 403                 if (smf_refresh_instance(AUDITD_FMRI) != 0) {
 404                         display_smf_error();
 405                         free(state);
 406                         exit(1);
 407                 }
 408         }
 409         free(state);
 410 }
 411 
 412 static void
 413 display_smf_error()
 414 {
 415         scf_error_t     rc = scf_error();
 416 
 417         switch (rc) {
 418         case SCF_ERROR_NOT_FOUND:
 419                 (void) fprintf(stderr,
 420                     "SMF error: \"%s\" not found.\n",
 421                     AUDITD_FMRI);
 422                 break;
 423         default:
 424                 (void) fprintf(stderr, "SMF error: %s\n", scf_strerror(rc));
 425                 break;
 426         }
 427 }