1186545Srwatson/*- 2189279Srwatson * Copyright (c) 2008-2009 Apple Inc. 3186545Srwatson * All rights reserved. 4186545Srwatson * 5186545Srwatson * Redistribution and use in source and binary forms, with or without 6186545Srwatson * modification, are permitted provided that the following conditions 7186545Srwatson * are met: 8186545Srwatson * 1. Redistributions of source code must retain the above copyright 9186545Srwatson * notice, this list of conditions and the following disclaimer. 10186545Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11186545Srwatson * notice, this list of conditions and the following disclaimer in the 12186545Srwatson * documentation and/or other materials provided with the distribution. 13186545Srwatson * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14186545Srwatson * its contributors may be used to endorse or promote products derived 15186545Srwatson * from this software without specific prior written permission. 16186545Srwatson * 17186545Srwatson * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND 18186545Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19186545Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20186545Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR 21186545Srwatson * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22186545Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23186545Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24186545Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25186545Srwatson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 26186545Srwatson * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27186545Srwatson * POSSIBILITY OF SUCH DAMAGE. 28186545Srwatson */ 29186545Srwatson 30186545Srwatson#include <sys/param.h> 31186545Srwatson 32186545Srwatson#include <config/config.h> 33186545Srwatson 34186545Srwatson#include <sys/dirent.h> 35186545Srwatson#ifdef HAVE_FULL_QUEUE_H 36186545Srwatson#include <sys/queue.h> 37186545Srwatson#else /* !HAVE_FULL_QUEUE_H */ 38186545Srwatson#include <compat/queue.h> 39186545Srwatson#endif /* !HAVE_FULL_QUEUE_H */ 40191273Srwatson#include <sys/mount.h> 41191273Srwatson#include <sys/socket.h> 42186545Srwatson 43186545Srwatson#include <sys/stat.h> 44186545Srwatson#include <sys/time.h> 45186545Srwatson 46186545Srwatson#include <netinet/in.h> 47186545Srwatson 48186545Srwatson#include <bsm/audit.h> 49186545Srwatson#include <bsm/audit_uevents.h> 50186545Srwatson#include <bsm/auditd_lib.h> 51186545Srwatson#include <bsm/libbsm.h> 52186545Srwatson 53243750Srwatson#include <assert.h> 54189279Srwatson#include <dirent.h> 55186545Srwatson#include <err.h> 56186545Srwatson#include <errno.h> 57186545Srwatson#include <fcntl.h> 58186545Srwatson#include <stdio.h> 59186545Srwatson#include <string.h> 60186545Srwatson#include <stdlib.h> 61186545Srwatson#include <time.h> 62186545Srwatson#include <unistd.h> 63186545Srwatson#include <netdb.h> 64186545Srwatson 65186545Srwatson#ifdef __APPLE__ 66186545Srwatson#include <notify.h> 67186545Srwatson#ifndef __BSM_INTERNAL_NOTIFY_KEY 68243750Srwatson#define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change" 69186545Srwatson#endif /* __BSM_INTERNAL_NOTIFY_KEY */ 70186545Srwatson#endif /* __APPLE__ */ 71186545Srwatson 72186545Srwatson/* 73186545Srwatson * XXX This is temporary until this is moved to <bsm/audit.h> and shared with 74186545Srwatson * the kernel. 75186545Srwatson */ 76186545Srwatson#ifndef AUDIT_HARD_LIMIT_FREE_BLOCKS 77186545Srwatson#define AUDIT_HARD_LIMIT_FREE_BLOCKS 4 78186545Srwatson#endif 79186545Srwatson 80189279Srwatson/* 81189279Srwatson * Number of seconds to January 1, 2000 82189279Srwatson */ 83189279Srwatson#define JAN_01_2000 946598400 84189279Srwatson 85186545Srwatsonstruct dir_ent { 86186545Srwatson char *dirname; 87186545Srwatson uint8_t softlim; 88186545Srwatson uint8_t hardlim; 89186545Srwatson TAILQ_ENTRY(dir_ent) dirs; 90186545Srwatson}; 91186545Srwatson 92186545Srwatsonstatic TAILQ_HEAD(, dir_ent) dir_q; 93186545Srwatson 94189279Srwatsonstruct audit_trail { 95189279Srwatson time_t at_time; 96189279Srwatson char *at_path; 97189279Srwatson off_t at_size; 98189279Srwatson 99189279Srwatson TAILQ_ENTRY(audit_trail) at_trls; 100189279Srwatson}; 101189279Srwatson 102189279Srwatsonstatic int auditd_minval = -1; 103243750Srwatsonstatic int auditd_dist = 0; 104189279Srwatson 105189279Srwatsonstatic char auditd_host[MAXHOSTNAMELEN]; 106189279Srwatsonstatic int auditd_hostlen = -1; 107189279Srwatson 108186545Srwatsonstatic char *auditd_errmsg[] = { 109243750Srwatson "no error", /* ADE_NOERR ( 0) */ 110243750Srwatson "could not parse audit_control(5) file", /* ADE_PARSE ( 1) */ 111243750Srwatson "auditon(2) failed", /* ADE_AUDITON ( 2) */ 112243750Srwatson "malloc(3) failed", /* ADE_NOMEM ( 3) */ 113243750Srwatson "all audit log directories over soft limit", /* ADE_SOFTLIM ( 4) */ 114243750Srwatson "all audit log directories over hard limit", /* ADE_HARDLIM ( 5) */ 115243750Srwatson "could not create file name string", /* ADE_STRERR ( 6) */ 116243750Srwatson "could not open audit record", /* ADE_AU_OPEN ( 7) */ 117243750Srwatson "could not close audit record", /* ADE_AU_CLOSE ( 8) */ 118243750Srwatson "could not set active audit session state", /* ADE_SETAUDIT ( 9) */ 119243750Srwatson "auditctl(2) failed (trail still swapped)", /* ADE_ACTL (10) */ 120243750Srwatson "auditctl(2) failed (trail not swapped)", /* ADE_ACTLERR (11) */ 121243750Srwatson "could not swap audit trail file", /* ADE_SWAPERR (12) */ 122186545Srwatson "could not rename crash recovery file", /* ADE_RENAME (13) */ 123186545Srwatson "could not read 'current' link file", /* ADE_READLINK (14) */ 124243750Srwatson "could not create 'current' link file", /* ADE_SYMLINK (15) */ 125186545Srwatson "invalid argument", /* ADE_INVAL (16) */ 126186545Srwatson "could not resolve hostname to address", /* ADE_GETADDR (17) */ 127186545Srwatson "address family not supported", /* ADE_ADDRFAM (18) */ 128189279Srwatson "error expiring audit trail files", /* ADE_EXPIRE (19) */ 129186545Srwatson}; 130186545Srwatson 131243750Srwatson#define MAXERRCODE (sizeof(auditd_errmsg) / sizeof(auditd_errmsg[0])) 132186545Srwatson 133243750Srwatson#define NA_EVENT_STR_SIZE 128 134243750Srwatson#define POL_STR_SIZE 128 135186545Srwatson 136186545Srwatson 137186545Srwatson/* 138186545Srwatson * Look up and return the error string for the given audit error code. 139186545Srwatson */ 140186545Srwatsonconst char * 141186545Srwatsonauditd_strerror(int errcode) 142186545Srwatson{ 143186545Srwatson int idx = -errcode; 144186545Srwatson 145186545Srwatson if (idx < 0 || idx > (int)MAXERRCODE) 146186545Srwatson return ("Invalid auditd error code"); 147243750Srwatson 148186545Srwatson return (auditd_errmsg[idx]); 149186545Srwatson} 150186545Srwatson 151186545Srwatson 152186545Srwatson/* 153243750Srwatson * Free our local list of directory names and init list. 154186545Srwatson */ 155186545Srwatsonstatic void 156186545Srwatsonfree_dir_q(void) 157186545Srwatson{ 158186545Srwatson struct dir_ent *d1, *d2; 159243750Srwatson 160186545Srwatson d1 = TAILQ_FIRST(&dir_q); 161186545Srwatson while (d1 != NULL) { 162186545Srwatson d2 = TAILQ_NEXT(d1, dirs); 163186545Srwatson free(d1->dirname); 164186545Srwatson free(d1); 165186545Srwatson d1 = d2; 166186545Srwatson } 167186545Srwatson TAILQ_INIT(&dir_q); 168186545Srwatson} 169186545Srwatson 170186545Srwatson/* 171186545Srwatson * Concat the directory name to the given file name. 172186545Srwatson * XXX We should affix the hostname also 173186545Srwatson */ 174186545Srwatsonstatic char * 175186545Srwatsonaffixdir(char *name, struct dir_ent *dirent) 176186545Srwatson{ 177186545Srwatson char *fn = NULL; 178186545Srwatson 179186545Srwatson /* 180186545Srwatson * Sanity check on file name. 181186545Srwatson */ 182243750Srwatson if (strlen(name) != FILENAME_LEN) { 183186545Srwatson errno = EINVAL; 184243750Srwatson return (NULL); 185186545Srwatson } 186186545Srwatson 187189279Srwatson /* 188189279Srwatson * If the host is set then also add the hostname to the filename. 189189279Srwatson */ 190189279Srwatson if (auditd_hostlen != -1) 191189279Srwatson asprintf(&fn, "%s/%s.%s", dirent->dirname, name, auditd_host); 192189279Srwatson else 193189279Srwatson asprintf(&fn, "%s/%s", dirent->dirname, name); 194186545Srwatson return (fn); 195186545Srwatson} 196186545Srwatson 197186545Srwatson/* 198186545Srwatson * Insert the directory entry in the list by the way they are ordered in 199186545Srwatson * audit_control(5). Move the entries that are over the soft and hard limits 200186545Srwatson * toward the tail. 201186545Srwatson */ 202186545Srwatsonstatic void 203186545Srwatsoninsert_orderly(struct dir_ent *denew) 204186545Srwatson{ 205186545Srwatson struct dir_ent *dep; 206243750Srwatson 207186545Srwatson TAILQ_FOREACH(dep, &dir_q, dirs) { 208186545Srwatson if (dep->softlim == 1 && denew->softlim == 0) { 209186545Srwatson TAILQ_INSERT_BEFORE(dep, denew, dirs); 210243750Srwatson return; 211186545Srwatson } 212186545Srwatson if (dep->hardlim == 1 && denew->hardlim == 0) { 213186545Srwatson TAILQ_INSERT_BEFORE(dep, denew, dirs); 214186545Srwatson return; 215186545Srwatson } 216186545Srwatson } 217186545Srwatson TAILQ_INSERT_TAIL(&dir_q, denew, dirs); 218186545Srwatson} 219186545Srwatson 220186545Srwatson/* 221243750Srwatson * Get the min percentage of free blocks from audit_control(5) and that 222243750Srwatson * value in the kernel. Return: 223243750Srwatson * ADE_NOERR on success, 224243750Srwatson * ADE_PARSE error parsing audit_control(5), 225243750Srwatson */ 226243750Srwatsonint 227243750Srwatsonauditd_set_dist(void) 228243750Srwatson{ 229243750Srwatson int ret; 230243750Srwatson 231243750Srwatson ret = getacdist(); 232243750Srwatson if (ret < 0) 233243750Srwatson return (ADE_PARSE); 234243750Srwatson 235243750Srwatson auditd_dist = ret; 236243750Srwatson 237243750Srwatson return (ADE_NOERR); 238243750Srwatson} 239243750Srwatson 240243750Srwatson/* 241186545Srwatson * Get the host from audit_control(5) and set it in the audit kernel 242186545Srwatson * information. Return: 243186545Srwatson * ADE_NOERR on success. 244186545Srwatson * ADE_PARSE error parsing audit_control(5). 245186545Srwatson * ADE_AUDITON error getting/setting auditon(2) value. 246243750Srwatson * ADE_GETADDR error getting address info for host. 247243750Srwatson * ADE_ADDRFAM un-supported address family. 248186545Srwatson */ 249186545Srwatsonint 250186545Srwatsonauditd_set_host(void) 251186545Srwatson{ 252186545Srwatson struct sockaddr_in6 *sin6; 253186545Srwatson struct sockaddr_in *sin; 254186545Srwatson struct addrinfo *res; 255186545Srwatson struct auditinfo_addr aia; 256186545Srwatson int error, ret = ADE_NOERR; 257186545Srwatson 258339093Sasomers if ((getachost(auditd_host, sizeof(auditd_host)) != 0) || 259339093Sasomers ((auditd_hostlen = strlen(auditd_host)) == 0)) { 260243750Srwatson ret = ADE_PARSE; 261243750Srwatson 262186545Srwatson /* 263186545Srwatson * To maintain reverse compatability with older audit_control 264186545Srwatson * files, simply drop a warning if the host parameter has not 265186545Srwatson * been set. However, we will explicitly disable the 266186545Srwatson * generation of extended audit header by passing in a zeroed 267186545Srwatson * termid structure. 268186545Srwatson */ 269186545Srwatson bzero(&aia, sizeof(aia)); 270186545Srwatson aia.ai_termid.at_type = AU_IPv4; 271191273Srwatson error = audit_set_kaudit(&aia, sizeof(aia)); 272186545Srwatson if (error < 0 && errno != ENOSYS) 273186545Srwatson ret = ADE_AUDITON; 274186545Srwatson return (ret); 275186545Srwatson } 276189279Srwatson error = getaddrinfo(auditd_host, NULL, NULL, &res); 277186545Srwatson if (error) 278186545Srwatson return (ADE_GETADDR); 279186545Srwatson switch (res->ai_family) { 280186545Srwatson case PF_INET6: 281186545Srwatson sin6 = (struct sockaddr_in6 *) res->ai_addr; 282186545Srwatson bcopy(&sin6->sin6_addr.s6_addr, 283186545Srwatson &aia.ai_termid.at_addr[0], sizeof(struct in6_addr)); 284186545Srwatson aia.ai_termid.at_type = AU_IPv6; 285186545Srwatson break; 286186545Srwatson 287186545Srwatson case PF_INET: 288186545Srwatson sin = (struct sockaddr_in *) res->ai_addr; 289186545Srwatson bcopy(&sin->sin_addr.s_addr, 290186545Srwatson &aia.ai_termid.at_addr[0], sizeof(struct in_addr)); 291186545Srwatson aia.ai_termid.at_type = AU_IPv4; 292186545Srwatson break; 293186545Srwatson 294186545Srwatson default: 295186545Srwatson /* Un-supported address family in host parameter. */ 296186545Srwatson errno = EAFNOSUPPORT; 297186545Srwatson return (ADE_ADDRFAM); 298186545Srwatson } 299186545Srwatson 300191273Srwatson if (audit_set_kaudit(&aia, sizeof(aia)) < 0) 301186545Srwatson ret = ADE_AUDITON; 302186545Srwatson 303186545Srwatson return (ret); 304186545Srwatson} 305186545Srwatson 306243750Srwatson/* 307186545Srwatson * Get the min percentage of free blocks from audit_control(5) and that 308186545Srwatson * value in the kernel. Return: 309186545Srwatson * ADE_NOERR on success, 310243750Srwatson * ADE_PARSE error parsing audit_control(5), 311186545Srwatson * ADE_AUDITON error getting/setting auditon(2) value. 312186545Srwatson */ 313186545Srwatsonint 314186545Srwatsonauditd_set_minfree(void) 315186545Srwatson{ 316186545Srwatson au_qctrl_t qctrl; 317186545Srwatson 318189279Srwatson if (getacmin(&auditd_minval) != 0) 319186545Srwatson return (ADE_PARSE); 320243750Srwatson 321191273Srwatson if (audit_get_qctrl(&qctrl, sizeof(qctrl)) != 0) 322186545Srwatson return (ADE_AUDITON); 323186545Srwatson 324189279Srwatson if (qctrl.aq_minfree != auditd_minval) { 325189279Srwatson qctrl.aq_minfree = auditd_minval; 326191273Srwatson if (audit_set_qctrl(&qctrl, sizeof(qctrl)) != 0) 327186545Srwatson return (ADE_AUDITON); 328186545Srwatson } 329186545Srwatson 330186545Srwatson return (0); 331186545Srwatson} 332186545Srwatson 333186545Srwatson/* 334189279Srwatson * Convert a trailname into a timestamp (seconds). Return 0 if the conversion 335189279Srwatson * was successful. 336189279Srwatson */ 337189279Srwatsonstatic int 338189279Srwatsontrailname_to_tstamp(char *fn, time_t *tstamp) 339189279Srwatson{ 340189279Srwatson struct tm tm; 341243750Srwatson char ts[TIMESTAMP_LEN + 1]; 342189279Srwatson char *p; 343189279Srwatson 344189279Srwatson *tstamp = 0; 345189279Srwatson 346189279Srwatson /* 347189279Srwatson * Get the ending time stamp. 348189279Srwatson */ 349189279Srwatson if ((p = strchr(fn, '.')) == NULL) 350189279Srwatson return (1); 351243750Srwatson strlcpy(ts, ++p, sizeof(ts)); 352189279Srwatson if (strlen(ts) != POSTFIX_LEN) 353189279Srwatson return (1); 354189279Srwatson 355189279Srwatson bzero(&tm, sizeof(tm)); 356189279Srwatson 357189279Srwatson /* seconds (0-60) */ 358189279Srwatson p = ts + POSTFIX_LEN - 2; 359189279Srwatson tm.tm_sec = atol(p); 360189279Srwatson if (tm.tm_sec < 0 || tm.tm_sec > 60) 361189279Srwatson return (1); 362189279Srwatson 363243750Srwatson /* minutes (0-59) */ 364189279Srwatson *p = '\0'; p -= 2; 365189279Srwatson tm.tm_min = atol(p); 366189279Srwatson if (tm.tm_min < 0 || tm.tm_min > 59) 367189279Srwatson return (1); 368189279Srwatson 369189279Srwatson /* hours (0 - 23) */ 370189279Srwatson *p = '\0'; p -= 2; 371189279Srwatson tm.tm_hour = atol(p); 372189279Srwatson if (tm.tm_hour < 0 || tm.tm_hour > 23) 373189279Srwatson return (1); 374189279Srwatson 375189279Srwatson /* day of month (1-31) */ 376189279Srwatson *p = '\0'; p -= 2; 377189279Srwatson tm.tm_mday = atol(p); 378189279Srwatson if (tm.tm_mday < 1 || tm.tm_mday > 31) 379189279Srwatson return (1); 380189279Srwatson 381189279Srwatson /* month (0 - 11) */ 382189279Srwatson *p = '\0'; p -= 2; 383189279Srwatson tm.tm_mon = atol(p) - 1; 384189279Srwatson if (tm.tm_mon < 0 || tm.tm_mon > 11) 385189279Srwatson return (1); 386189279Srwatson 387189279Srwatson /* year (year - 1900) */ 388189279Srwatson *p = '\0'; p -= 4; 389189279Srwatson tm.tm_year = atol(p) - 1900; 390189279Srwatson if (tm.tm_year < 0) 391189279Srwatson return (1); 392189279Srwatson 393189279Srwatson *tstamp = timegm(&tm); 394189279Srwatson 395189279Srwatson return (0); 396189279Srwatson} 397189279Srwatson 398189279Srwatson/* 399189279Srwatson * Remove audit trails files according to the expiration conditions. Returns: 400243750Srwatson * ADE_NOERR on success or there is nothing to do. 401243750Srwatson * ADE_PARSE if error parsing audit_control(5). 402243750Srwatson * ADE_NOMEM if could not allocate memory. 403292432Sbrueffer * ADE_READLINK if could not read link file. 404292432Sbrueffer * ADE_EXPIRE if there was an unexpected error. 405189279Srwatson */ 406189279Srwatsonint 407189279Srwatsonauditd_expire_trails(int (*warn_expired)(char *)) 408189279Srwatson{ 409292432Sbrueffer int andflg, len, ret = ADE_NOERR; 410189279Srwatson size_t expire_size, total_size = 0L; 411189279Srwatson time_t expire_age, oldest_time, current_time = time(NULL); 412189279Srwatson struct dir_ent *traildir; 413189279Srwatson struct audit_trail *at; 414189279Srwatson char *afnp, *pn; 415189279Srwatson TAILQ_HEAD(au_trls_head, audit_trail) head = 416189279Srwatson TAILQ_HEAD_INITIALIZER(head); 417189279Srwatson struct stat stbuf; 418189279Srwatson char activefn[MAXPATHLEN]; 419189279Srwatson 420189279Srwatson /* 421189279Srwatson * Read the expiration conditions. If no conditions then return no 422189279Srwatson * error. 423189279Srwatson */ 424189279Srwatson if (getacexpire(&andflg, &expire_age, &expire_size) < 0) 425189279Srwatson return (ADE_PARSE); 426189279Srwatson if (!expire_age && !expire_size) 427189279Srwatson return (ADE_NOERR); 428189279Srwatson 429189279Srwatson /* 430189279Srwatson * Read the 'current' trail file name. Trim off directory path. 431189279Srwatson */ 432189279Srwatson activefn[0] = '\0'; 433292432Sbrueffer len = readlink(AUDIT_CURRENT_LINK, activefn, MAXPATHLEN - 1); 434292432Sbrueffer if (len < 0) 435292432Sbrueffer return (ADE_READLINK); 436243750Srwatson if ((afnp = strrchr(activefn, '/')) != NULL) 437189279Srwatson afnp++; 438189279Srwatson 439189279Srwatson 440189279Srwatson /* 441189279Srwatson * Build tail queue of the trail files. 442189279Srwatson */ 443189279Srwatson TAILQ_FOREACH(traildir, &dir_q, dirs) { 444189279Srwatson DIR *dirp; 445189279Srwatson struct dirent *dp; 446189279Srwatson 447189279Srwatson dirp = opendir(traildir->dirname); 448189279Srwatson while ((dp = readdir(dirp)) != NULL) { 449189279Srwatson time_t tstamp = 0; 450189279Srwatson struct audit_trail *new; 451189279Srwatson 452189279Srwatson /* 453189279Srwatson * Quickly filter non-trail files. 454189279Srwatson */ 455243750Srwatson if (dp->d_namlen < FILENAME_LEN || 456189279Srwatson dp->d_name[POSTFIX_LEN] != '.') 457189279Srwatson continue; 458189279Srwatson 459189279Srwatson if (asprintf(&pn, "%s/%s", traildir->dirname, 460243750Srwatson dp->d_name) < 0) { 461189279Srwatson ret = ADE_NOMEM; 462189279Srwatson break; 463189279Srwatson } 464189279Srwatson 465189279Srwatson if (stat(pn, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 466189279Srwatson free(pn); 467189279Srwatson continue; 468189279Srwatson } 469189279Srwatson 470189279Srwatson total_size += stbuf.st_size; 471189279Srwatson 472189279Srwatson /* 473189279Srwatson * If this is the 'current' audit trail then 474189279Srwatson * don't add it to the tail queue. 475189279Srwatson */ 476243750Srwatson if (NULL != afnp && strcmp(dp->d_name, afnp) == 0) { 477189279Srwatson free(pn); 478189279Srwatson continue; 479189279Srwatson } 480189279Srwatson 481189279Srwatson /* 482189279Srwatson * Get the ending time stamp encoded in the trail 483189279Srwatson * name. If we can't read it or if it is older 484189279Srwatson * than Jan 1, 2000 then use the mtime. 485189279Srwatson */ 486189279Srwatson if (trailname_to_tstamp(dp->d_name, &tstamp) != 0 || 487189279Srwatson tstamp < JAN_01_2000) 488189279Srwatson tstamp = stbuf.st_mtime; 489189279Srwatson 490189279Srwatson /* 491189279Srwatson * If the time stamp is older than Jan 1, 2000 then 492189279Srwatson * update the mtime of the trail file to the current 493189279Srwatson * time. This is so we don't prematurely remove a trail 494189279Srwatson * file that was created while the system clock reset 495189279Srwatson * to the * "beginning of time" but later the system 496189279Srwatson * clock is set to the correct current time. 497189279Srwatson */ 498189279Srwatson if (current_time >= JAN_01_2000 && 499189279Srwatson tstamp < JAN_01_2000) { 500189279Srwatson struct timeval tv[2]; 501189279Srwatson 502189279Srwatson tstamp = stbuf.st_mtime = current_time; 503243750Srwatson TIMESPEC_TO_TIMEVAL(&tv[0], 504189279Srwatson &stbuf.st_atimespec); 505243750Srwatson TIMESPEC_TO_TIMEVAL(&tv[1], 506189279Srwatson &stbuf.st_mtimespec); 507189279Srwatson utimes(pn, tv); 508189279Srwatson } 509189279Srwatson 510189279Srwatson /* 511189279Srwatson * Allocate and populate the new entry. 512189279Srwatson */ 513189279Srwatson new = malloc(sizeof(*new)); 514189279Srwatson if (NULL == new) { 515189279Srwatson free(pn); 516189279Srwatson ret = ADE_NOMEM; 517189279Srwatson break; 518189279Srwatson } 519189279Srwatson new->at_time = tstamp; 520189279Srwatson new->at_size = stbuf.st_size; 521189279Srwatson new->at_path = pn; 522189279Srwatson 523189279Srwatson /* 524189279Srwatson * Check to see if we have a new head. Otherwise, 525189279Srwatson * walk the tailq from the tail first and do a simple 526189279Srwatson * insertion sort. 527189279Srwatson */ 528189279Srwatson if (TAILQ_EMPTY(&head) || 529243750Srwatson new->at_time <= TAILQ_FIRST(&head)->at_time) { 530189279Srwatson TAILQ_INSERT_HEAD(&head, new, at_trls); 531189279Srwatson continue; 532189279Srwatson } 533189279Srwatson 534189279Srwatson TAILQ_FOREACH_REVERSE(at, &head, au_trls_head, at_trls) 535189279Srwatson if (new->at_time >= at->at_time) { 536189279Srwatson TAILQ_INSERT_AFTER(&head, at, new, 537189279Srwatson at_trls); 538189279Srwatson break; 539189279Srwatson } 540189279Srwatson 541189279Srwatson } 542234034Srwatson closedir(dirp); 543189279Srwatson } 544189279Srwatson 545189279Srwatson oldest_time = current_time - expire_age; 546189279Srwatson 547243750Srwatson /* 548189279Srwatson * Expire trail files, oldest (mtime) first, if the given 549189279Srwatson * conditions are met. 550189279Srwatson */ 551189279Srwatson at = TAILQ_FIRST(&head); 552189279Srwatson while (NULL != at) { 553189279Srwatson struct audit_trail *at_next = TAILQ_NEXT(at, at_trls); 554189279Srwatson 555189279Srwatson if (andflg) { 556189279Srwatson if ((expire_size && total_size > expire_size) && 557189279Srwatson (expire_age && at->at_time < oldest_time)) { 558189279Srwatson if (warn_expired) 559243750Srwatson (*warn_expired)(at->at_path); 560189279Srwatson if (unlink(at->at_path) < 0) 561189279Srwatson ret = ADE_EXPIRE; 562189279Srwatson total_size -= at->at_size; 563189279Srwatson } 564189279Srwatson } else { 565189279Srwatson if ((expire_size && total_size > expire_size) || 566189279Srwatson (expire_age && at->at_time < oldest_time)) { 567189279Srwatson if (warn_expired) 568243750Srwatson (*warn_expired)(at->at_path); 569189279Srwatson if (unlink(at->at_path) < 0) 570189279Srwatson ret = ADE_EXPIRE; 571189279Srwatson total_size -= at->at_size; 572189279Srwatson } 573189279Srwatson } 574189279Srwatson 575189279Srwatson free(at->at_path); 576189279Srwatson free(at); 577189279Srwatson at = at_next; 578189279Srwatson } 579189279Srwatson 580189279Srwatson return (ret); 581189279Srwatson} 582189279Srwatson 583189279Srwatson/* 584186545Srwatson * Parses the "dir" entry in audit_control(5) into an ordered list. Also, will 585189279Srwatson * set the minfree and host values if not already set. Arguments include 586189279Srwatson * function pointers to audit_warn functions for soft and hard limits. Returns: 587186545Srwatson * ADE_NOERR on success, 588186545Srwatson * ADE_PARSE error parsing audit_control(5), 589186545Srwatson * ADE_AUDITON error getting/setting auditon(2) value, 590186545Srwatson * ADE_NOMEM error allocating memory, 591186545Srwatson * ADE_SOFTLIM if all the directories are over the soft limit, 592186545Srwatson * ADE_HARDLIM if all the directories are over the hard limit, 593186545Srwatson */ 594186545Srwatsonint 595186545Srwatsonauditd_read_dirs(int (*warn_soft)(char *), int (*warn_hard)(char *)) 596186545Srwatson{ 597186545Srwatson char cur_dir[MAXNAMLEN]; 598186545Srwatson struct dir_ent *dirent; 599186545Srwatson struct statfs sfs; 600186545Srwatson int err; 601186545Srwatson char soft, hard; 602186545Srwatson int tcnt = 0; 603186545Srwatson int scnt = 0; 604186545Srwatson int hcnt = 0; 605186545Srwatson 606189279Srwatson if (auditd_minval == -1 && (err = auditd_set_minfree()) != 0) 607186545Srwatson return (err); 608186545Srwatson 609189279Srwatson if (auditd_hostlen == -1) 610189279Srwatson auditd_set_host(); 611189279Srwatson 612243750Srwatson /* 613243750Srwatson * Init directory q. Force a re-read of the file the next time. 614243750Srwatson */ 615186545Srwatson free_dir_q(); 616186545Srwatson endac(); 617186545Srwatson 618186545Srwatson /* 619186545Srwatson * Read the list of directories into an ordered linked list 620186545Srwatson * admin's preference, then those over soft limit and, finally, 621186545Srwatson * those over the hard limit. 622186545Srwatson * 623243750Srwatson * XXX We should use the reentrant interfaces once they are 624243750Srwatson * available. 625243750Srwatson */ 626186545Srwatson while (getacdir(cur_dir, MAXNAMLEN) >= 0) { 627186545Srwatson if (statfs(cur_dir, &sfs) < 0) 628186545Srwatson continue; /* XXX should warn */ 629243750Srwatson soft = (sfs.f_bfree < (sfs.f_blocks * auditd_minval / 100 )) ? 630189279Srwatson 1 : 0; 631186545Srwatson hard = (sfs.f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) ? 1 : 0; 632186545Srwatson if (soft) { 633243750Srwatson if (warn_soft) 634186545Srwatson (*warn_soft)(cur_dir); 635186545Srwatson scnt++; 636186545Srwatson } 637186545Srwatson if (hard) { 638186545Srwatson if (warn_hard) 639186545Srwatson (*warn_hard)(cur_dir); 640186545Srwatson hcnt++; 641186545Srwatson } 642186545Srwatson dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent)); 643186545Srwatson if (dirent == NULL) 644186545Srwatson return (ADE_NOMEM); 645186545Srwatson dirent->softlim = soft; 646243750Srwatson dirent->hardlim = hard; 647186545Srwatson dirent->dirname = (char *) malloc(MAXNAMLEN); 648186545Srwatson if (dirent->dirname == NULL) { 649186545Srwatson free(dirent); 650186545Srwatson return (ADE_NOMEM); 651186545Srwatson } 652186545Srwatson strlcpy(dirent->dirname, cur_dir, MAXNAMLEN); 653186545Srwatson insert_orderly(dirent); 654186545Srwatson tcnt++; 655186545Srwatson } 656186545Srwatson 657186545Srwatson if (hcnt == tcnt) 658186545Srwatson return (ADE_HARDLIM); 659186545Srwatson if (scnt == tcnt) 660186545Srwatson return (ADE_SOFTLIM); 661186545Srwatson return (0); 662186545Srwatson} 663186545Srwatson 664186545Srwatsonvoid 665186545Srwatsonauditd_close_dirs(void) 666186545Srwatson{ 667186545Srwatson free_dir_q(); 668189279Srwatson auditd_minval = -1; 669189279Srwatson auditd_hostlen = -1; 670186545Srwatson} 671186545Srwatson 672186545Srwatson 673186545Srwatson/* 674186545Srwatson * Process the audit event file, obtaining a class mapping for each event, and 675186545Srwatson * set that mapping into the kernel. Return: 676243750Srwatson * n number of event mappings that were successfully processed, 677243750Srwatson * ADE_NOMEM if there was an error allocating memory. 678186545Srwatson */ 679186545Srwatsonint 680186545Srwatsonauditd_set_evcmap(void) 681186545Srwatson{ 682186545Srwatson au_event_ent_t ev, *evp; 683186545Srwatson au_evclass_map_t evc_map; 684186545Srwatson int ctr = 0; 685186545Srwatson 686186545Srwatson /* 687186545Srwatson * XXX There's a risk here that the BSM library will return NULL 688186545Srwatson * for an event when it can't properly map it to a class. In that 689186545Srwatson * case, we will not process any events beyond the one that failed, 690186545Srwatson * but should. We need a way to get a count of the events. 691186545Srwatson */ 692186545Srwatson ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX); 693186545Srwatson ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX); 694243750Srwatson if (ev.ae_name == NULL || ev.ae_desc == NULL) { 695186545Srwatson if (ev.ae_name != NULL) 696186545Srwatson free(ev.ae_name); 697186545Srwatson return (ADE_NOMEM); 698186545Srwatson } 699243750Srwatson 700186545Srwatson /* 701186545Srwatson * XXXRW: Currently we have no way to remove mappings from the kernel 702186545Srwatson * when they are removed from the file-based mappings. 703186545Srwatson */ 704186545Srwatson evp = &ev; 705186545Srwatson setauevent(); 706186545Srwatson while ((evp = getauevent_r(evp)) != NULL) { 707186545Srwatson evc_map.ec_number = evp->ae_number; 708186545Srwatson evc_map.ec_class = evp->ae_class; 709191273Srwatson if (audit_set_class(&evc_map, sizeof(evc_map)) == 0) 710186545Srwatson ctr++; 711186545Srwatson } 712186545Srwatson endauevent(); 713186545Srwatson free(ev.ae_name); 714186545Srwatson free(ev.ae_desc); 715186545Srwatson 716186545Srwatson return (ctr); 717186545Srwatson} 718186545Srwatson 719186545Srwatson/* 720186545Srwatson * Get the non-attributable event string and set the kernel mask. Return: 721243750Srwatson * ADE_NOERR on success, 722186545Srwatson * ADE_PARSE error parsing audit_control(5), 723186545Srwatson * ADE_AUDITON error setting the mask using auditon(2). 724186545Srwatson */ 725186545Srwatsonint 726186545Srwatsonauditd_set_namask(void) 727186545Srwatson{ 728186545Srwatson au_mask_t aumask; 729186545Srwatson char naeventstr[NA_EVENT_STR_SIZE]; 730243750Srwatson 731243750Srwatson if (getacna(naeventstr, NA_EVENT_STR_SIZE) != 0 || 732243750Srwatson getauditflagsbin(naeventstr, &aumask) != 0) 733186545Srwatson return (ADE_PARSE); 734186545Srwatson 735191273Srwatson if (audit_set_kmask(&aumask, sizeof(aumask)) != 0) 736186545Srwatson return (ADE_AUDITON); 737186545Srwatson 738186545Srwatson return (ADE_NOERR); 739186545Srwatson} 740186545Srwatson 741186545Srwatson/* 742186545Srwatson * Set the audit control policy if a policy is configured in audit_control(5), 743186545Srwatson * implement the policy. However, if one isn't defined or if there is an error 744186545Srwatson * parsing the control file, set AUDIT_CNT to avoid leaving the system in a 745186545Srwatson * fragile state. Return: 746243750Srwatson * ADE_NOERR on success, 747186545Srwatson * ADE_PARSE error parsing audit_control(5), 748186545Srwatson * ADE_AUDITON error setting policy using auditon(2). 749186545Srwatson */ 750186545Srwatsonint 751186545Srwatsonauditd_set_policy(void) 752186545Srwatson{ 753191273Srwatson int policy; 754186545Srwatson char polstr[POL_STR_SIZE]; 755186545Srwatson 756243750Srwatson if (getacpol(polstr, POL_STR_SIZE) != 0 || 757243750Srwatson au_strtopol(polstr, &policy) != 0) { 758186545Srwatson policy = AUDIT_CNT; 759191273Srwatson if (audit_set_policy(&policy) != 0) 760186545Srwatson return (ADE_AUDITON); 761186545Srwatson return (ADE_PARSE); 762243750Srwatson } 763186545Srwatson 764191273Srwatson if (audit_set_policy(&policy) != 0) 765186545Srwatson return (ADE_AUDITON); 766186545Srwatson 767186545Srwatson return (ADE_NOERR); 768186545Srwatson} 769186545Srwatson 770243750Srwatson/* 771186545Srwatson * Set trail rotation size. Return: 772243750Srwatson * ADE_NOERR on success, 773186545Srwatson * ADE_PARSE error parsing audit_control(5), 774186545Srwatson * ADE_AUDITON error setting file size using auditon(2). 775186545Srwatson */ 776186545Srwatsonint 777186545Srwatsonauditd_set_fsize(void) 778186545Srwatson{ 779186545Srwatson size_t filesz; 780186545Srwatson au_fstat_t au_fstat; 781186545Srwatson 782186545Srwatson /* 783186545Srwatson * Set trail rotation size. 784186545Srwatson */ 785186545Srwatson if (getacfilesz(&filesz) != 0) 786186545Srwatson return (ADE_PARSE); 787186545Srwatson 788186545Srwatson bzero(&au_fstat, sizeof(au_fstat)); 789186545Srwatson au_fstat.af_filesz = filesz; 790191273Srwatson if (audit_set_fsize(&au_fstat, sizeof(au_fstat)) != 0) 791186545Srwatson return (ADE_AUDITON); 792186545Srwatson 793243750Srwatson return (ADE_NOERR); 794186545Srwatson} 795186545Srwatson 796243750Srwatsonstatic void 797243750Srwatsoninject_dist(const char *fromname, char *toname, size_t tonamesize) 798243750Srwatson{ 799243750Srwatson char *ptr; 800243750Srwatson 801243750Srwatson ptr = strrchr(fromname, '/'); 802243750Srwatson assert(ptr != NULL); 803243750Srwatson assert(ptr - fromname < (ssize_t)tonamesize); 804243750Srwatson strlcpy(toname, fromname, ptr - fromname + 1); 805243750Srwatson strlcat(toname, "/dist/", tonamesize); 806243750Srwatson strlcat(toname, ptr + 1, tonamesize); 807243750Srwatson} 808243750Srwatson 809243750Srwatsonstatic int 810243750Srwatsonauditdist_link(const char *filename) 811243750Srwatson{ 812243750Srwatson char fname[MAXPATHLEN]; 813243750Srwatson 814243750Srwatson if (auditd_dist) { 815243750Srwatson inject_dist(filename, fname, sizeof(fname)); 816243750Srwatson /* Ignore errors. */ 817243750Srwatson (void) link(filename, fname); 818243750Srwatson } 819243750Srwatson 820243750Srwatson return (0); 821243750Srwatson} 822243750Srwatson 823243750Srwatsonint 824243750Srwatsonauditd_rename(const char *fromname, const char *toname) 825243750Srwatson{ 826243750Srwatson char fname[MAXPATHLEN], tname[MAXPATHLEN]; 827243750Srwatson 828243750Srwatson if (auditd_dist) { 829243750Srwatson inject_dist(fromname, fname, sizeof(fname)); 830243750Srwatson inject_dist(toname, tname, sizeof(tname)); 831243750Srwatson /* Ignore errors. */ 832243750Srwatson (void) rename(fname, tname); 833243750Srwatson } 834243750Srwatson 835243750Srwatson return (rename(fromname, toname)); 836243750Srwatson} 837243750Srwatson 838186545Srwatson/* 839243750Srwatson * Create the new audit file with appropriate permissions and ownership. 840243750Srwatson * Call auditctl(2) for this file. 841243750Srwatson * Try to clean up if something goes wrong. 842243750Srwatson * *errorp is modified only on auditctl(2) failure. 843186545Srwatson */ 844186545Srwatsonstatic int 845243750Srwatsonopen_trail(char *fname, gid_t gid, int *errorp) 846186545Srwatson{ 847243750Srwatson int fd; 848243750Srwatson 849243750Srwatson /* XXXPJD: What should we do if the file already exists? */ 850243750Srwatson fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR); 851186545Srwatson if (fd < 0) 852186545Srwatson return (-1); 853243750Srwatson if (fchown(fd, -1, gid) < 0 || fchmod(fd, S_IRUSR | S_IRGRP) < 0) { 854243750Srwatson (void) close(fd); 855243750Srwatson (void) unlink(fname); 856186545Srwatson return (-1); 857186545Srwatson } 858243750Srwatson (void) close(fd); 859243750Srwatson if (auditctl(fname) < 0) { 860243750Srwatson *errorp = errno; 861243750Srwatson (void) unlink(fname); 862243750Srwatson return (-1); 863243750Srwatson } 864243750Srwatson (void) auditdist_link(fname); 865243750Srwatson return (0); 866186545Srwatson} 867186545Srwatson 868186545Srwatson/* 869186545Srwatson * Create the new audit trail file, swap with existing audit file. Arguments 870186545Srwatson * include timestamp for the filename, a pointer to a string for returning the 871243750Srwatson * new file name, GID for trail file, and audit_warn function pointer for 872186545Srwatson * 'getacdir()' errors. Returns: 873243750Srwatson * ADE_NOERR on success, 874243750Srwatson * ADE_STRERR if the file name string could not be created, 875243750Srwatson * ADE_SWAPERR if the audit trail file could not be swapped, 876243750Srwatson * ADE_ACTL if the auditctl(2) call failed but file swap still 877186545Srwatson * successful. 878186545Srwatson * ADE_ACTLERR if the auditctl(2) call failed and file swap failed. 879186545Srwatson * ADE_SYMLINK if symlink(2) failed updating the current link. 880186545Srwatson */ 881186545Srwatsonint 882243750Srwatsonauditd_swap_trail(char *TS, char **newfile, gid_t gid, 883186545Srwatson int (*warn_getacdir)(char *)) 884186545Srwatson{ 885243750Srwatson char timestr[FILENAME_LEN + 1]; 886186545Srwatson char *fn; 887186545Srwatson struct dir_ent *dirent; 888186545Srwatson int saverrno = 0; 889243750Srwatson 890243750Srwatson if (strlen(TS) != TIMESTAMP_LEN || 891243750Srwatson snprintf(timestr, sizeof(timestr), "%s.%s", TS, 892243750Srwatson NOT_TERMINATED) < 0) { 893186545Srwatson errno = EINVAL; 894186545Srwatson return (ADE_STRERR); 895186545Srwatson } 896243750Srwatson 897186545Srwatson /* Try until we succeed. */ 898189279Srwatson TAILQ_FOREACH(dirent, &dir_q, dirs) { 899243750Srwatson if (dirent->hardlim) 900186545Srwatson continue; 901186545Srwatson if ((fn = affixdir(timestr, dirent)) == NULL) 902186545Srwatson return (ADE_STRERR); 903186545Srwatson 904186545Srwatson /* 905243750Srwatson * Create the file and pass to the kernel if all went well. 906186545Srwatson */ 907243750Srwatson if (open_trail(fn, gid, &saverrno) == 0) { 908243750Srwatson /* Success. */ 909243750Srwatson *newfile = fn; 910243750Srwatson if (saverrno) { 911243750Srwatson /* 912243750Srwatson * auditctl() failed but still 913243750Srwatson * successful. Return errno and "soft" 914243750Srwatson * error. 915186545Srwatson */ 916243750Srwatson errno = saverrno; 917243750Srwatson return (ADE_ACTL); 918243750Srwatson } 919243750Srwatson return (ADE_NOERR); 920243750Srwatson } 921186545Srwatson /* 922243750Srwatson * auditctl failed setting log file. Try again. 923243750Srwatson */ 924243750Srwatson /* 925186545Srwatson * Tell the administrator about lack of permissions for dir. 926186545Srwatson */ 927186545Srwatson if (warn_getacdir != NULL) 928186545Srwatson (*warn_getacdir)(dirent->dirname); 929186545Srwatson } 930186545Srwatson if (saverrno) { 931186545Srwatson errno = saverrno; 932186545Srwatson return (ADE_ACTLERR); 933186545Srwatson } else 934186545Srwatson return (ADE_SWAPERR); 935186545Srwatson} 936186545Srwatson 937186545Srwatson/* 938186545Srwatson * Mask calling process from being audited. Returns: 939186545Srwatson * ADE_NOERR on success, 940186545Srwatson * ADE_SETAUDIT if setaudit(2) fails. 941186545Srwatson */ 942189279Srwatson#ifdef __APPLE__ 943186545Srwatsonint 944186545Srwatsonauditd_prevent_audit(void) 945186545Srwatson{ 946189279Srwatson auditinfo_addr_t aia; 947189279Srwatson 948243750Srwatson /* 949189279Srwatson * To prevent event feedback cycles and avoid audit becoming stalled if 950189279Srwatson * auditing is suspended we mask this processes events from being 951189279Srwatson * audited. We allow the uid, tid, and mask fields to be implicitly 952243750Srwatson * set to zero, but do set the audit session ID to the PID. 953189279Srwatson * 954189279Srwatson * XXXRW: Is there more to it than this? 955189279Srwatson */ 956189279Srwatson bzero(&aia, sizeof(aia)); 957189279Srwatson aia.ai_asid = AU_ASSIGN_ASID; 958189279Srwatson aia.ai_termid.at_type = AU_IPv4; 959189279Srwatson if (setaudit_addr(&aia, sizeof(aia)) != 0) 960243750Srwatson return (ADE_SETAUDIT); 961189279Srwatson return (ADE_NOERR); 962189279Srwatson} 963189279Srwatson#else 964189279Srwatsonint 965189279Srwatsonauditd_prevent_audit(void) 966189279Srwatson{ 967186545Srwatson auditinfo_t ai; 968186545Srwatson 969243750Srwatson /* 970186545Srwatson * To prevent event feedback cycles and avoid audit becoming stalled if 971186545Srwatson * auditing is suspended we mask this processes events from being 972186545Srwatson * audited. We allow the uid, tid, and mask fields to be implicitly 973243750Srwatson * set to zero, but do set the audit session ID to the PID. 974186545Srwatson * 975186545Srwatson * XXXRW: Is there more to it than this? 976186545Srwatson */ 977186545Srwatson bzero(&ai, sizeof(ai)); 978186545Srwatson ai.ai_asid = getpid(); 979186545Srwatson if (setaudit(&ai) != 0) 980243750Srwatson return (ADE_SETAUDIT); 981186545Srwatson return (ADE_NOERR); 982186545Srwatson} 983243750Srwatson#endif /* !__APPLE__ */ 984186545Srwatson 985186545Srwatson/* 986186545Srwatson * Generate and submit audit record for audit startup or shutdown. The event 987186545Srwatson * argument can be AUE_audit_recovery, AUE_audit_startup or 988186545Srwatson * AUE_audit_shutdown. The path argument will add a path token, if not NULL. 989186545Srwatson * Returns: 990186545Srwatson * AUE_NOERR on success, 991186545Srwatson * ADE_NOMEM if memory allocation fails, 992243750Srwatson * ADE_AU_OPEN if au_open(3) fails, 993186545Srwatson * ADE_AU_CLOSE if au_close(3) fails. 994186545Srwatson */ 995186545Srwatsonint 996186545Srwatsonauditd_gen_record(int event, char *path) 997186545Srwatson{ 998186545Srwatson int aufd; 999186545Srwatson uid_t uid; 1000186545Srwatson pid_t pid; 1001186545Srwatson char *autext = NULL; 1002186545Srwatson token_t *tok; 1003186545Srwatson struct auditinfo_addr aia; 1004186545Srwatson 1005186545Srwatson if (event == AUE_audit_startup) 1006186545Srwatson asprintf(&autext, "%s::Audit startup", getprogname()); 1007186545Srwatson else if (event == AUE_audit_shutdown) 1008186545Srwatson asprintf(&autext, "%s::Audit shutdown", getprogname()); 1009186545Srwatson else if (event == AUE_audit_recovery) 1010186545Srwatson asprintf(&autext, "%s::Audit recovery", getprogname()); 1011243750Srwatson else 1012186545Srwatson return (ADE_INVAL); 1013186545Srwatson if (autext == NULL) 1014186545Srwatson return (ADE_NOMEM); 1015186545Srwatson 1016186545Srwatson if ((aufd = au_open()) == -1) { 1017186545Srwatson free(autext); 1018186545Srwatson return (ADE_AU_OPEN); 1019186545Srwatson } 1020186545Srwatson bzero(&aia, sizeof(aia)); 1021186545Srwatson uid = getuid(); pid = getpid(); 1022186545Srwatson if ((tok = au_to_subject32_ex(uid, geteuid(), getegid(), uid, getgid(), 1023243750Srwatson pid, pid, &aia.ai_termid)) != NULL) 1024186545Srwatson au_write(aufd, tok); 1025186545Srwatson if ((tok = au_to_text(autext)) != NULL) 1026186545Srwatson au_write(aufd, tok); 1027186545Srwatson free(autext); 1028186545Srwatson if (path != NULL && (tok = au_to_path(path)) != NULL) 1029186545Srwatson au_write(aufd, tok); 1030186545Srwatson if ((tok = au_to_return32(0, 0)) != NULL) 1031186545Srwatson au_write(aufd, tok); 1032186545Srwatson if (au_close(aufd, 1, event) == -1) 1033186545Srwatson return (ADE_AU_CLOSE); 1034186545Srwatson 1035186545Srwatson return (ADE_NOERR); 1036186545Srwatson} 1037186545Srwatson 1038186545Srwatson/* 1039186545Srwatson * Check for a 'current' symlink and do crash recovery, if needed. Create a new 1040186545Srwatson * 'current' symlink. The argument 'curfile' is the file the 'current' symlink 1041186545Srwatson * should point to. Returns: 1042186545Srwatson * ADE_NOERR on success, 1043243750Srwatson * ADE_AU_OPEN if au_open(3) fails, 1044243750Srwatson * ADE_AU_CLOSE if au_close(3) fails. 1045186545Srwatson * ADE_RENAME if error renaming audit trail file, 1046186545Srwatson * ADE_READLINK if error reading the 'current' link, 1047186545Srwatson * ADE_SYMLINK if error creating 'current' link. 1048186545Srwatson */ 1049186545Srwatsonint 1050186545Srwatsonauditd_new_curlink(char *curfile) 1051186545Srwatson{ 1052186545Srwatson int len, err; 1053186545Srwatson char *ptr; 1054186545Srwatson char *path = NULL; 1055186545Srwatson struct stat sb; 1056186545Srwatson char recoveredname[MAXPATHLEN]; 1057186545Srwatson char newname[MAXPATHLEN]; 1058186545Srwatson 1059186545Srwatson /* 1060186545Srwatson * Check to see if audit was shutdown properly. If not, clean up, 1061186545Srwatson * recover previous audit trail file, and generate audit record. 1062186545Srwatson */ 1063243750Srwatson len = readlink(AUDIT_CURRENT_LINK, recoveredname, 1064243750Srwatson sizeof(recoveredname) - 1); 1065186545Srwatson if (len > 0) { 1066186545Srwatson /* 'current' exist but is it pointing at a valid file? */ 1067186545Srwatson recoveredname[len++] = '\0'; 1068243750Srwatson if (stat(recoveredname, &sb) == 0) { 1069186545Srwatson /* Yes, rename it to a crash recovery file. */ 1070243750Srwatson strlcpy(newname, recoveredname, sizeof(newname)); 1071186545Srwatson 1072186545Srwatson if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) { 1073189279Srwatson memcpy(ptr, CRASH_RECOVERY, POSTFIX_LEN); 1074243750Srwatson if (auditd_rename(recoveredname, newname) != 0) 1075186545Srwatson return (ADE_RENAME); 1076186545Srwatson } else 1077186545Srwatson return (ADE_STRERR); 1078186545Srwatson 1079186545Srwatson path = newname; 1080186545Srwatson } 1081186545Srwatson 1082186545Srwatson /* 'current' symlink is (now) invalid so remove it. */ 1083186545Srwatson (void) unlink(AUDIT_CURRENT_LINK); 1084186545Srwatson 1085186545Srwatson /* Note the crash recovery in current audit trail */ 1086186545Srwatson err = auditd_gen_record(AUE_audit_recovery, path); 1087186545Srwatson if (err) 1088186545Srwatson return (err); 1089186545Srwatson } 1090186545Srwatson 1091186545Srwatson if (len < 0 && errno != ENOENT) 1092186545Srwatson return (ADE_READLINK); 1093186545Srwatson 1094186545Srwatson if (symlink(curfile, AUDIT_CURRENT_LINK) != 0) 1095186545Srwatson return (ADE_SYMLINK); 1096186545Srwatson 1097186545Srwatson return (0); 1098186545Srwatson} 1099186545Srwatson 1100186545Srwatson/* 1101186545Srwatson * Do just what we need to quickly start auditing. Assume no system logging or 1102186545Srwatson * notify. Return: 1103186545Srwatson * 0 on success, 1104186545Srwatson * -1 on failure. 1105186545Srwatson */ 1106186545Srwatsonint 1107186545Srwatsonaudit_quick_start(void) 1108186545Srwatson{ 1109186545Srwatson int err; 1110189279Srwatson char *newfile = NULL; 1111186545Srwatson time_t tt; 1112243750Srwatson char TS[TIMESTAMP_LEN + 1]; 1113189279Srwatson int ret = 0; 1114186545Srwatson 1115243750Srwatson /* 1116186545Srwatson * Mask auditing of this process. 1117186545Srwatson */ 1118186545Srwatson if (auditd_prevent_audit() != 0) 1119186545Srwatson return (-1); 1120186545Srwatson 1121186545Srwatson /* 1122186545Srwatson * Read audit_control and get log directories. 1123186545Srwatson */ 1124243750Srwatson err = auditd_read_dirs(NULL, NULL); 1125186545Srwatson if (err != ADE_NOERR && err != ADE_SOFTLIM) 1126186545Srwatson return (-1); 1127186545Srwatson 1128186545Srwatson /* 1129243750Srwatson * Setup trail file distribution. 1130243750Srwatson */ 1131243750Srwatson (void) auditd_set_dist(); 1132243750Srwatson 1133243750Srwatson /* 1134186545Srwatson * Create a new audit trail log. 1135186545Srwatson */ 1136243750Srwatson if (getTSstr(tt, TS, sizeof(TS)) != 0) 1137186545Srwatson return (-1); 1138186545Srwatson err = auditd_swap_trail(TS, &newfile, getgid(), NULL); 1139189279Srwatson if (err != ADE_NOERR && err != ADE_ACTL) { 1140189279Srwatson ret = -1; 1141189279Srwatson goto out; 1142189279Srwatson } 1143186545Srwatson 1144186545Srwatson /* 1145243750Srwatson * Add the current symlink and recover from crash, if needed. 1146186545Srwatson */ 1147189279Srwatson if (auditd_new_curlink(newfile) != 0) { 1148189279Srwatson ret = -1; 1149189279Srwatson goto out; 1150189279Srwatson } 1151186545Srwatson 1152186545Srwatson /* 1153186545Srwatson * At this point auditing has started so generate audit start-up record. 1154186545Srwatson */ 1155189279Srwatson if (auditd_gen_record(AUE_audit_startup, NULL) != 0) { 1156189279Srwatson ret = -1; 1157189279Srwatson goto out; 1158189279Srwatson } 1159186545Srwatson 1160186545Srwatson /* 1161186545Srwatson * Configure the audit controls. 1162186545Srwatson */ 1163186545Srwatson (void) auditd_set_evcmap(); 1164186545Srwatson (void) auditd_set_namask(); 1165186545Srwatson (void) auditd_set_policy(); 1166186545Srwatson (void) auditd_set_fsize(); 1167186545Srwatson (void) auditd_set_minfree(); 1168186545Srwatson (void) auditd_set_host(); 1169186545Srwatson 1170189279Srwatsonout: 1171189279Srwatson if (newfile != NULL) 1172189279Srwatson free(newfile); 1173189279Srwatson 1174189279Srwatson return (ret); 1175186545Srwatson} 1176186545Srwatson 1177186545Srwatson/* 1178186545Srwatson * Shut down auditing quickly. Assumes that is only called on system shutdown. 1179186545Srwatson * Returns: 1180186545Srwatson * 0 on success, 1181186545Srwatson * -1 on failure. 1182186545Srwatson */ 1183186545Srwatsonint 1184186545Srwatsonaudit_quick_stop(void) 1185186545Srwatson{ 1186186545Srwatson int len; 1187191273Srwatson int cond; 1188186545Srwatson char *ptr; 1189186545Srwatson time_t tt; 1190186545Srwatson char oldname[MAXPATHLEN]; 1191186545Srwatson char newname[MAXPATHLEN]; 1192243750Srwatson char TS[TIMESTAMP_LEN + 1]; 1193186545Srwatson 1194186545Srwatson /* 1195186545Srwatson * Auditing already disabled? 1196186545Srwatson */ 1197191273Srwatson if (audit_get_cond(&cond) != 0) 1198186545Srwatson return (-1); 1199187214Srwatson if (cond == AUC_NOAUDIT) 1200186545Srwatson return (0); 1201186545Srwatson 1202186545Srwatson /* 1203186545Srwatson * Generate audit shutdown record. 1204186545Srwatson */ 1205186545Srwatson (void) auditd_gen_record(AUE_audit_shutdown, NULL); 1206186545Srwatson 1207186545Srwatson /* 1208186545Srwatson * Shutdown auditing in the kernel. 1209186545Srwatson */ 1210186545Srwatson cond = AUC_DISABLED; 1211191273Srwatson if (audit_set_cond(&cond) != 0) 1212186545Srwatson return (-1); 1213186545Srwatson#ifdef __BSM_INTERNAL_NOTIFY_KEY 1214186545Srwatson notify_post(__BSM_INTERNAL_NOTIFY_KEY); 1215186545Srwatson#endif 1216186545Srwatson 1217186545Srwatson /* 1218186545Srwatson * Rename last audit trail and remove 'current' link. 1219186545Srwatson */ 1220243750Srwatson len = readlink(AUDIT_CURRENT_LINK, oldname, sizeof(oldname) - 1); 1221186545Srwatson if (len < 0) 1222186545Srwatson return (-1); 1223186545Srwatson oldname[len++] = '\0'; 1224186545Srwatson 1225243750Srwatson if (getTSstr(tt, TS, sizeof(TS)) != 0) 1226186545Srwatson return (-1); 1227186545Srwatson 1228243750Srwatson strlcpy(newname, oldname, sizeof(newname)); 1229186545Srwatson 1230186545Srwatson if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) { 1231189279Srwatson memcpy(ptr, TS, POSTFIX_LEN); 1232243750Srwatson if (auditd_rename(oldname, newname) != 0) 1233186545Srwatson return (-1); 1234186545Srwatson } else 1235186545Srwatson return (-1); 1236243750Srwatson 1237186545Srwatson (void) unlink(AUDIT_CURRENT_LINK); 1238186545Srwatson 1239186545Srwatson return (0); 1240186545Srwatson} 1241