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