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 * 29243750Srwatson * $P4: //depot/projects/trustedbsd/openbsm/libauditd/auditd_lib.c#18 $ 30186545Srwatson */ 31186545Srwatson 32186545Srwatson#include <sys/param.h> 33186545Srwatson 34186545Srwatson#include <config/config.h> 35186545Srwatson 36186545Srwatson#include <sys/dirent.h> 37186545Srwatson#ifdef HAVE_FULL_QUEUE_H 38186545Srwatson#include <sys/queue.h> 39186545Srwatson#else /* !HAVE_FULL_QUEUE_H */ 40186545Srwatson#include <compat/queue.h> 41186545Srwatson#endif /* !HAVE_FULL_QUEUE_H */ 42191273Srwatson#include <sys/mount.h> 43191273Srwatson#include <sys/socket.h> 44186545Srwatson 45186545Srwatson#include <sys/stat.h> 46186545Srwatson#include <sys/time.h> 47186545Srwatson 48186545Srwatson#include <netinet/in.h> 49186545Srwatson 50186545Srwatson#include <bsm/audit.h> 51186545Srwatson#include <bsm/audit_uevents.h> 52186545Srwatson#include <bsm/auditd_lib.h> 53186545Srwatson#include <bsm/libbsm.h> 54186545Srwatson 55243750Srwatson#include <assert.h> 56189279Srwatson#include <dirent.h> 57186545Srwatson#include <err.h> 58186545Srwatson#include <errno.h> 59186545Srwatson#include <fcntl.h> 60186545Srwatson#include <stdio.h> 61186545Srwatson#include <string.h> 62186545Srwatson#include <stdlib.h> 63186545Srwatson#include <time.h> 64186545Srwatson#include <unistd.h> 65186545Srwatson#include <netdb.h> 66186545Srwatson 67186545Srwatson#ifdef __APPLE__ 68186545Srwatson#include <notify.h> 69186545Srwatson#ifndef __BSM_INTERNAL_NOTIFY_KEY 70243750Srwatson#define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change" 71186545Srwatson#endif /* __BSM_INTERNAL_NOTIFY_KEY */ 72186545Srwatson#endif /* __APPLE__ */ 73186545Srwatson 74186545Srwatson/* 75186545Srwatson * XXX This is temporary until this is moved to <bsm/audit.h> and shared with 76186545Srwatson * the kernel. 77186545Srwatson */ 78186545Srwatson#ifndef AUDIT_HARD_LIMIT_FREE_BLOCKS 79186545Srwatson#define AUDIT_HARD_LIMIT_FREE_BLOCKS 4 80186545Srwatson#endif 81186545Srwatson 82189279Srwatson/* 83189279Srwatson * Number of seconds to January 1, 2000 84189279Srwatson */ 85189279Srwatson#define JAN_01_2000 946598400 86189279Srwatson 87186545Srwatsonstruct dir_ent { 88186545Srwatson char *dirname; 89186545Srwatson uint8_t softlim; 90186545Srwatson uint8_t hardlim; 91186545Srwatson TAILQ_ENTRY(dir_ent) dirs; 92186545Srwatson}; 93186545Srwatson 94186545Srwatsonstatic TAILQ_HEAD(, dir_ent) dir_q; 95186545Srwatson 96189279Srwatsonstruct audit_trail { 97189279Srwatson time_t at_time; 98189279Srwatson char *at_path; 99189279Srwatson off_t at_size; 100189279Srwatson 101189279Srwatson TAILQ_ENTRY(audit_trail) at_trls; 102189279Srwatson}; 103189279Srwatson 104189279Srwatsonstatic int auditd_minval = -1; 105243750Srwatsonstatic int auditd_dist = 0; 106189279Srwatson 107189279Srwatsonstatic char auditd_host[MAXHOSTNAMELEN]; 108189279Srwatsonstatic int auditd_hostlen = -1; 109189279Srwatson 110186545Srwatsonstatic char *auditd_errmsg[] = { 111243750Srwatson "no error", /* ADE_NOERR ( 0) */ 112243750Srwatson "could not parse audit_control(5) file", /* ADE_PARSE ( 1) */ 113243750Srwatson "auditon(2) failed", /* ADE_AUDITON ( 2) */ 114243750Srwatson "malloc(3) failed", /* ADE_NOMEM ( 3) */ 115243750Srwatson "all audit log directories over soft limit", /* ADE_SOFTLIM ( 4) */ 116243750Srwatson "all audit log directories over hard limit", /* ADE_HARDLIM ( 5) */ 117243750Srwatson "could not create file name string", /* ADE_STRERR ( 6) */ 118243750Srwatson "could not open audit record", /* ADE_AU_OPEN ( 7) */ 119243750Srwatson "could not close audit record", /* ADE_AU_CLOSE ( 8) */ 120243750Srwatson "could not set active audit session state", /* ADE_SETAUDIT ( 9) */ 121243750Srwatson "auditctl(2) failed (trail still swapped)", /* ADE_ACTL (10) */ 122243750Srwatson "auditctl(2) failed (trail not swapped)", /* ADE_ACTLERR (11) */ 123243750Srwatson "could not swap audit trail file", /* ADE_SWAPERR (12) */ 124186545Srwatson "could not rename crash recovery file", /* ADE_RENAME (13) */ 125186545Srwatson "could not read 'current' link file", /* ADE_READLINK (14) */ 126243750Srwatson "could not create 'current' link file", /* ADE_SYMLINK (15) */ 127186545Srwatson "invalid argument", /* ADE_INVAL (16) */ 128186545Srwatson "could not resolve hostname to address", /* ADE_GETADDR (17) */ 129186545Srwatson "address family not supported", /* ADE_ADDRFAM (18) */ 130189279Srwatson "error expiring audit trail files", /* ADE_EXPIRE (19) */ 131186545Srwatson}; 132186545Srwatson 133243750Srwatson#define MAXERRCODE (sizeof(auditd_errmsg) / sizeof(auditd_errmsg[0])) 134186545Srwatson 135243750Srwatson#define NA_EVENT_STR_SIZE 128 136243750Srwatson#define POL_STR_SIZE 128 137186545Srwatson 138186545Srwatson 139186545Srwatson/* 140186545Srwatson * Look up and return the error string for the given audit error code. 141186545Srwatson */ 142186545Srwatsonconst char * 143186545Srwatsonauditd_strerror(int errcode) 144186545Srwatson{ 145186545Srwatson int idx = -errcode; 146186545Srwatson 147186545Srwatson if (idx < 0 || idx > (int)MAXERRCODE) 148186545Srwatson return ("Invalid auditd error code"); 149243750Srwatson 150186545Srwatson return (auditd_errmsg[idx]); 151186545Srwatson} 152186545Srwatson 153186545Srwatson 154186545Srwatson/* 155243750Srwatson * Free our local list of directory names and init list. 156186545Srwatson */ 157186545Srwatsonstatic void 158186545Srwatsonfree_dir_q(void) 159186545Srwatson{ 160186545Srwatson struct dir_ent *d1, *d2; 161243750Srwatson 162186545Srwatson d1 = TAILQ_FIRST(&dir_q); 163186545Srwatson while (d1 != NULL) { 164186545Srwatson d2 = TAILQ_NEXT(d1, dirs); 165186545Srwatson free(d1->dirname); 166186545Srwatson free(d1); 167186545Srwatson d1 = d2; 168186545Srwatson } 169186545Srwatson TAILQ_INIT(&dir_q); 170186545Srwatson} 171186545Srwatson 172186545Srwatson/* 173186545Srwatson * Concat the directory name to the given file name. 174186545Srwatson * XXX We should affix the hostname also 175186545Srwatson */ 176186545Srwatsonstatic char * 177186545Srwatsonaffixdir(char *name, struct dir_ent *dirent) 178186545Srwatson{ 179186545Srwatson char *fn = NULL; 180186545Srwatson 181186545Srwatson /* 182186545Srwatson * Sanity check on file name. 183186545Srwatson */ 184243750Srwatson if (strlen(name) != FILENAME_LEN) { 185186545Srwatson errno = EINVAL; 186243750Srwatson return (NULL); 187186545Srwatson } 188186545Srwatson 189189279Srwatson /* 190189279Srwatson * If the host is set then also add the hostname to the filename. 191189279Srwatson */ 192189279Srwatson if (auditd_hostlen != -1) 193189279Srwatson asprintf(&fn, "%s/%s.%s", dirent->dirname, name, auditd_host); 194189279Srwatson else 195189279Srwatson asprintf(&fn, "%s/%s", dirent->dirname, name); 196186545Srwatson return (fn); 197186545Srwatson} 198186545Srwatson 199186545Srwatson/* 200186545Srwatson * Insert the directory entry in the list by the way they are ordered in 201186545Srwatson * audit_control(5). Move the entries that are over the soft and hard limits 202186545Srwatson * toward the tail. 203186545Srwatson */ 204186545Srwatsonstatic void 205186545Srwatsoninsert_orderly(struct dir_ent *denew) 206186545Srwatson{ 207186545Srwatson struct dir_ent *dep; 208243750Srwatson 209186545Srwatson TAILQ_FOREACH(dep, &dir_q, dirs) { 210186545Srwatson if (dep->softlim == 1 && denew->softlim == 0) { 211186545Srwatson TAILQ_INSERT_BEFORE(dep, denew, dirs); 212243750Srwatson return; 213186545Srwatson } 214186545Srwatson if (dep->hardlim == 1 && denew->hardlim == 0) { 215186545Srwatson TAILQ_INSERT_BEFORE(dep, denew, dirs); 216186545Srwatson return; 217186545Srwatson } 218186545Srwatson } 219186545Srwatson TAILQ_INSERT_TAIL(&dir_q, denew, dirs); 220186545Srwatson} 221186545Srwatson 222186545Srwatson/* 223243750Srwatson * Get the min percentage of free blocks from audit_control(5) and that 224243750Srwatson * value in the kernel. Return: 225243750Srwatson * ADE_NOERR on success, 226243750Srwatson * ADE_PARSE error parsing audit_control(5), 227243750Srwatson */ 228243750Srwatsonint 229243750Srwatsonauditd_set_dist(void) 230243750Srwatson{ 231243750Srwatson int ret; 232243750Srwatson 233243750Srwatson ret = getacdist(); 234243750Srwatson if (ret < 0) 235243750Srwatson return (ADE_PARSE); 236243750Srwatson 237243750Srwatson auditd_dist = ret; 238243750Srwatson 239243750Srwatson return (ADE_NOERR); 240243750Srwatson} 241243750Srwatson 242243750Srwatson/* 243186545Srwatson * Get the host from audit_control(5) and set it in the audit kernel 244186545Srwatson * information. Return: 245186545Srwatson * ADE_NOERR on success. 246186545Srwatson * ADE_PARSE error parsing audit_control(5). 247186545Srwatson * ADE_AUDITON error getting/setting auditon(2) value. 248243750Srwatson * ADE_GETADDR error getting address info for host. 249243750Srwatson * ADE_ADDRFAM un-supported address family. 250186545Srwatson */ 251186545Srwatsonint 252186545Srwatsonauditd_set_host(void) 253186545Srwatson{ 254186545Srwatson struct sockaddr_in6 *sin6; 255186545Srwatson struct sockaddr_in *sin; 256186545Srwatson struct addrinfo *res; 257186545Srwatson struct auditinfo_addr aia; 258186545Srwatson int error, ret = ADE_NOERR; 259186545Srwatson 260189279Srwatson if (getachost(auditd_host, sizeof(auditd_host)) != 0) { 261243750Srwatson ret = ADE_PARSE; 262243750Srwatson 263186545Srwatson /* 264186545Srwatson * To maintain reverse compatability with older audit_control 265186545Srwatson * files, simply drop a warning if the host parameter has not 266186545Srwatson * been set. However, we will explicitly disable the 267186545Srwatson * generation of extended audit header by passing in a zeroed 268186545Srwatson * termid structure. 269186545Srwatson */ 270186545Srwatson bzero(&aia, sizeof(aia)); 271186545Srwatson aia.ai_termid.at_type = AU_IPv4; 272191273Srwatson error = audit_set_kaudit(&aia, sizeof(aia)); 273186545Srwatson if (error < 0 && errno != ENOSYS) 274186545Srwatson ret = ADE_AUDITON; 275186545Srwatson return (ret); 276186545Srwatson } 277189279Srwatson auditd_hostlen = strlen(auditd_host); 278189279Srwatson error = getaddrinfo(auditd_host, NULL, NULL, &res); 279186545Srwatson if (error) 280186545Srwatson return (ADE_GETADDR); 281186545Srwatson switch (res->ai_family) { 282186545Srwatson case PF_INET6: 283186545Srwatson sin6 = (struct sockaddr_in6 *) res->ai_addr; 284186545Srwatson bcopy(&sin6->sin6_addr.s6_addr, 285186545Srwatson &aia.ai_termid.at_addr[0], sizeof(struct in6_addr)); 286186545Srwatson aia.ai_termid.at_type = AU_IPv6; 287186545Srwatson break; 288186545Srwatson 289186545Srwatson case PF_INET: 290186545Srwatson sin = (struct sockaddr_in *) res->ai_addr; 291186545Srwatson bcopy(&sin->sin_addr.s_addr, 292186545Srwatson &aia.ai_termid.at_addr[0], sizeof(struct in_addr)); 293186545Srwatson aia.ai_termid.at_type = AU_IPv4; 294186545Srwatson break; 295186545Srwatson 296186545Srwatson default: 297186545Srwatson /* Un-supported address family in host parameter. */ 298186545Srwatson errno = EAFNOSUPPORT; 299186545Srwatson return (ADE_ADDRFAM); 300186545Srwatson } 301186545Srwatson 302191273Srwatson if (audit_set_kaudit(&aia, sizeof(aia)) < 0) 303186545Srwatson ret = ADE_AUDITON; 304186545Srwatson 305186545Srwatson return (ret); 306186545Srwatson} 307186545Srwatson 308243750Srwatson/* 309186545Srwatson * Get the min percentage of free blocks from audit_control(5) and that 310186545Srwatson * value in the kernel. Return: 311186545Srwatson * ADE_NOERR on success, 312243750Srwatson * ADE_PARSE error parsing audit_control(5), 313186545Srwatson * ADE_AUDITON error getting/setting auditon(2) value. 314186545Srwatson */ 315186545Srwatsonint 316186545Srwatsonauditd_set_minfree(void) 317186545Srwatson{ 318186545Srwatson au_qctrl_t qctrl; 319186545Srwatson 320189279Srwatson if (getacmin(&auditd_minval) != 0) 321186545Srwatson return (ADE_PARSE); 322243750Srwatson 323191273Srwatson if (audit_get_qctrl(&qctrl, sizeof(qctrl)) != 0) 324186545Srwatson return (ADE_AUDITON); 325186545Srwatson 326189279Srwatson if (qctrl.aq_minfree != auditd_minval) { 327189279Srwatson qctrl.aq_minfree = auditd_minval; 328191273Srwatson if (audit_set_qctrl(&qctrl, sizeof(qctrl)) != 0) 329186545Srwatson return (ADE_AUDITON); 330186545Srwatson } 331186545Srwatson 332186545Srwatson return (0); 333186545Srwatson} 334186545Srwatson 335186545Srwatson/* 336189279Srwatson * Convert a trailname into a timestamp (seconds). Return 0 if the conversion 337189279Srwatson * was successful. 338189279Srwatson */ 339189279Srwatsonstatic int 340189279Srwatsontrailname_to_tstamp(char *fn, time_t *tstamp) 341189279Srwatson{ 342189279Srwatson struct tm tm; 343243750Srwatson char ts[TIMESTAMP_LEN + 1]; 344189279Srwatson char *p; 345189279Srwatson 346189279Srwatson *tstamp = 0; 347189279Srwatson 348189279Srwatson /* 349189279Srwatson * Get the ending time stamp. 350189279Srwatson */ 351189279Srwatson if ((p = strchr(fn, '.')) == NULL) 352189279Srwatson return (1); 353243750Srwatson strlcpy(ts, ++p, sizeof(ts)); 354189279Srwatson if (strlen(ts) != POSTFIX_LEN) 355189279Srwatson return (1); 356189279Srwatson 357189279Srwatson bzero(&tm, sizeof(tm)); 358189279Srwatson 359189279Srwatson /* seconds (0-60) */ 360189279Srwatson p = ts + POSTFIX_LEN - 2; 361189279Srwatson tm.tm_sec = atol(p); 362189279Srwatson if (tm.tm_sec < 0 || tm.tm_sec > 60) 363189279Srwatson return (1); 364189279Srwatson 365243750Srwatson /* minutes (0-59) */ 366189279Srwatson *p = '\0'; p -= 2; 367189279Srwatson tm.tm_min = atol(p); 368189279Srwatson if (tm.tm_min < 0 || tm.tm_min > 59) 369189279Srwatson return (1); 370189279Srwatson 371189279Srwatson /* hours (0 - 23) */ 372189279Srwatson *p = '\0'; p -= 2; 373189279Srwatson tm.tm_hour = atol(p); 374189279Srwatson if (tm.tm_hour < 0 || tm.tm_hour > 23) 375189279Srwatson return (1); 376189279Srwatson 377189279Srwatson /* day of month (1-31) */ 378189279Srwatson *p = '\0'; p -= 2; 379189279Srwatson tm.tm_mday = atol(p); 380189279Srwatson if (tm.tm_mday < 1 || tm.tm_mday > 31) 381189279Srwatson return (1); 382189279Srwatson 383189279Srwatson /* month (0 - 11) */ 384189279Srwatson *p = '\0'; p -= 2; 385189279Srwatson tm.tm_mon = atol(p) - 1; 386189279Srwatson if (tm.tm_mon < 0 || tm.tm_mon > 11) 387189279Srwatson return (1); 388189279Srwatson 389189279Srwatson /* year (year - 1900) */ 390189279Srwatson *p = '\0'; p -= 4; 391189279Srwatson tm.tm_year = atol(p) - 1900; 392189279Srwatson if (tm.tm_year < 0) 393189279Srwatson return (1); 394189279Srwatson 395189279Srwatson *tstamp = timegm(&tm); 396189279Srwatson 397189279Srwatson return (0); 398189279Srwatson} 399189279Srwatson 400189279Srwatson/* 401189279Srwatson * Remove audit trails files according to the expiration conditions. Returns: 402243750Srwatson * ADE_NOERR on success or there is nothing to do. 403243750Srwatson * ADE_PARSE if error parsing audit_control(5). 404243750Srwatson * ADE_NOMEM if could not allocate memory. 405243750Srwatson * ADE_EXPIRE if there was an unespected error. 406189279Srwatson */ 407189279Srwatsonint 408189279Srwatsonauditd_expire_trails(int (*warn_expired)(char *)) 409189279Srwatson{ 410189279Srwatson int andflg, ret = ADE_NOERR; 411189279Srwatson size_t expire_size, total_size = 0L; 412189279Srwatson time_t expire_age, oldest_time, current_time = time(NULL); 413189279Srwatson struct dir_ent *traildir; 414189279Srwatson struct audit_trail *at; 415189279Srwatson char *afnp, *pn; 416189279Srwatson TAILQ_HEAD(au_trls_head, audit_trail) head = 417189279Srwatson TAILQ_HEAD_INITIALIZER(head); 418189279Srwatson struct stat stbuf; 419189279Srwatson char activefn[MAXPATHLEN]; 420189279Srwatson 421189279Srwatson /* 422189279Srwatson * Read the expiration conditions. If no conditions then return no 423189279Srwatson * error. 424189279Srwatson */ 425189279Srwatson if (getacexpire(&andflg, &expire_age, &expire_size) < 0) 426189279Srwatson return (ADE_PARSE); 427189279Srwatson if (!expire_age && !expire_size) 428189279Srwatson return (ADE_NOERR); 429189279Srwatson 430189279Srwatson /* 431189279Srwatson * Read the 'current' trail file name. Trim off directory path. 432189279Srwatson */ 433189279Srwatson activefn[0] = '\0'; 434189279Srwatson readlink(AUDIT_CURRENT_LINK, activefn, MAXPATHLEN - 1); 435243750Srwatson if ((afnp = strrchr(activefn, '/')) != NULL) 436189279Srwatson afnp++; 437189279Srwatson 438189279Srwatson 439189279Srwatson /* 440189279Srwatson * Build tail queue of the trail files. 441189279Srwatson */ 442189279Srwatson TAILQ_FOREACH(traildir, &dir_q, dirs) { 443189279Srwatson DIR *dirp; 444189279Srwatson struct dirent *dp; 445189279Srwatson 446189279Srwatson dirp = opendir(traildir->dirname); 447189279Srwatson while ((dp = readdir(dirp)) != NULL) { 448189279Srwatson time_t tstamp = 0; 449189279Srwatson struct audit_trail *new; 450189279Srwatson 451189279Srwatson /* 452189279Srwatson * Quickly filter non-trail files. 453189279Srwatson */ 454243750Srwatson if (dp->d_namlen < FILENAME_LEN || 455189279Srwatson dp->d_name[POSTFIX_LEN] != '.') 456189279Srwatson continue; 457189279Srwatson 458189279Srwatson if (asprintf(&pn, "%s/%s", traildir->dirname, 459243750Srwatson dp->d_name) < 0) { 460189279Srwatson ret = ADE_NOMEM; 461189279Srwatson break; 462189279Srwatson } 463189279Srwatson 464189279Srwatson if (stat(pn, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 465189279Srwatson free(pn); 466189279Srwatson continue; 467189279Srwatson } 468189279Srwatson 469189279Srwatson total_size += stbuf.st_size; 470189279Srwatson 471189279Srwatson /* 472189279Srwatson * If this is the 'current' audit trail then 473189279Srwatson * don't add it to the tail queue. 474189279Srwatson */ 475243750Srwatson if (NULL != afnp && strcmp(dp->d_name, afnp) == 0) { 476189279Srwatson free(pn); 477189279Srwatson continue; 478189279Srwatson } 479189279Srwatson 480189279Srwatson /* 481189279Srwatson * Get the ending time stamp encoded in the trail 482189279Srwatson * name. If we can't read it or if it is older 483189279Srwatson * than Jan 1, 2000 then use the mtime. 484189279Srwatson */ 485189279Srwatson if (trailname_to_tstamp(dp->d_name, &tstamp) != 0 || 486189279Srwatson tstamp < JAN_01_2000) 487189279Srwatson tstamp = stbuf.st_mtime; 488189279Srwatson 489189279Srwatson /* 490189279Srwatson * If the time stamp is older than Jan 1, 2000 then 491189279Srwatson * update the mtime of the trail file to the current 492189279Srwatson * time. This is so we don't prematurely remove a trail 493189279Srwatson * file that was created while the system clock reset 494189279Srwatson * to the * "beginning of time" but later the system 495189279Srwatson * clock is set to the correct current time. 496189279Srwatson */ 497189279Srwatson if (current_time >= JAN_01_2000 && 498189279Srwatson tstamp < JAN_01_2000) { 499189279Srwatson struct timeval tv[2]; 500189279Srwatson 501189279Srwatson tstamp = stbuf.st_mtime = current_time; 502243750Srwatson TIMESPEC_TO_TIMEVAL(&tv[0], 503189279Srwatson &stbuf.st_atimespec); 504243750Srwatson TIMESPEC_TO_TIMEVAL(&tv[1], 505189279Srwatson &stbuf.st_mtimespec); 506189279Srwatson utimes(pn, tv); 507189279Srwatson } 508189279Srwatson 509189279Srwatson /* 510189279Srwatson * Allocate and populate the new entry. 511189279Srwatson */ 512189279Srwatson new = malloc(sizeof(*new)); 513189279Srwatson if (NULL == new) { 514189279Srwatson free(pn); 515189279Srwatson ret = ADE_NOMEM; 516189279Srwatson break; 517189279Srwatson } 518189279Srwatson new->at_time = tstamp; 519189279Srwatson new->at_size = stbuf.st_size; 520189279Srwatson new->at_path = pn; 521189279Srwatson 522189279Srwatson /* 523189279Srwatson * Check to see if we have a new head. Otherwise, 524189279Srwatson * walk the tailq from the tail first and do a simple 525189279Srwatson * insertion sort. 526189279Srwatson */ 527189279Srwatson if (TAILQ_EMPTY(&head) || 528243750Srwatson new->at_time <= TAILQ_FIRST(&head)->at_time) { 529189279Srwatson TAILQ_INSERT_HEAD(&head, new, at_trls); 530189279Srwatson continue; 531189279Srwatson } 532189279Srwatson 533189279Srwatson TAILQ_FOREACH_REVERSE(at, &head, au_trls_head, at_trls) 534189279Srwatson if (new->at_time >= at->at_time) { 535189279Srwatson TAILQ_INSERT_AFTER(&head, at, new, 536189279Srwatson at_trls); 537189279Srwatson break; 538189279Srwatson } 539189279Srwatson 540189279Srwatson } 541234034Srwatson closedir(dirp); 542189279Srwatson } 543189279Srwatson 544189279Srwatson oldest_time = current_time - expire_age; 545189279Srwatson 546243750Srwatson /* 547189279Srwatson * Expire trail files, oldest (mtime) first, if the given 548189279Srwatson * conditions are met. 549189279Srwatson */ 550189279Srwatson at = TAILQ_FIRST(&head); 551189279Srwatson while (NULL != at) { 552189279Srwatson struct audit_trail *at_next = TAILQ_NEXT(at, at_trls); 553189279Srwatson 554189279Srwatson if (andflg) { 555189279Srwatson if ((expire_size && total_size > expire_size) && 556189279Srwatson (expire_age && at->at_time < oldest_time)) { 557189279Srwatson if (warn_expired) 558243750Srwatson (*warn_expired)(at->at_path); 559189279Srwatson if (unlink(at->at_path) < 0) 560189279Srwatson ret = ADE_EXPIRE; 561189279Srwatson total_size -= at->at_size; 562189279Srwatson } 563189279Srwatson } else { 564189279Srwatson if ((expire_size && total_size > expire_size) || 565189279Srwatson (expire_age && at->at_time < oldest_time)) { 566189279Srwatson if (warn_expired) 567243750Srwatson (*warn_expired)(at->at_path); 568189279Srwatson if (unlink(at->at_path) < 0) 569189279Srwatson ret = ADE_EXPIRE; 570189279Srwatson total_size -= at->at_size; 571189279Srwatson } 572189279Srwatson } 573189279Srwatson 574189279Srwatson free(at->at_path); 575189279Srwatson free(at); 576189279Srwatson at = at_next; 577189279Srwatson } 578189279Srwatson 579189279Srwatson return (ret); 580189279Srwatson} 581189279Srwatson 582189279Srwatson/* 583186545Srwatson * Parses the "dir" entry in audit_control(5) into an ordered list. Also, will 584189279Srwatson * set the minfree and host values if not already set. Arguments include 585189279Srwatson * function pointers to audit_warn functions for soft and hard limits. Returns: 586186545Srwatson * ADE_NOERR on success, 587186545Srwatson * ADE_PARSE error parsing audit_control(5), 588186545Srwatson * ADE_AUDITON error getting/setting auditon(2) value, 589186545Srwatson * ADE_NOMEM error allocating memory, 590186545Srwatson * ADE_SOFTLIM if all the directories are over the soft limit, 591186545Srwatson * ADE_HARDLIM if all the directories are over the hard limit, 592186545Srwatson */ 593186545Srwatsonint 594186545Srwatsonauditd_read_dirs(int (*warn_soft)(char *), int (*warn_hard)(char *)) 595186545Srwatson{ 596186545Srwatson char cur_dir[MAXNAMLEN]; 597186545Srwatson struct dir_ent *dirent; 598186545Srwatson struct statfs sfs; 599186545Srwatson int err; 600186545Srwatson char soft, hard; 601186545Srwatson int tcnt = 0; 602186545Srwatson int scnt = 0; 603186545Srwatson int hcnt = 0; 604186545Srwatson 605189279Srwatson if (auditd_minval == -1 && (err = auditd_set_minfree()) != 0) 606186545Srwatson return (err); 607186545Srwatson 608189279Srwatson if (auditd_hostlen == -1) 609189279Srwatson auditd_set_host(); 610189279Srwatson 611243750Srwatson /* 612243750Srwatson * Init directory q. Force a re-read of the file the next time. 613243750Srwatson */ 614186545Srwatson free_dir_q(); 615186545Srwatson endac(); 616186545Srwatson 617186545Srwatson /* 618186545Srwatson * Read the list of directories into an ordered linked list 619186545Srwatson * admin's preference, then those over soft limit and, finally, 620186545Srwatson * those over the hard limit. 621186545Srwatson * 622243750Srwatson * XXX We should use the reentrant interfaces once they are 623243750Srwatson * available. 624243750Srwatson */ 625186545Srwatson while (getacdir(cur_dir, MAXNAMLEN) >= 0) { 626186545Srwatson if (statfs(cur_dir, &sfs) < 0) 627186545Srwatson continue; /* XXX should warn */ 628243750Srwatson soft = (sfs.f_bfree < (sfs.f_blocks * auditd_minval / 100 )) ? 629189279Srwatson 1 : 0; 630186545Srwatson hard = (sfs.f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) ? 1 : 0; 631186545Srwatson if (soft) { 632243750Srwatson if (warn_soft) 633186545Srwatson (*warn_soft)(cur_dir); 634186545Srwatson scnt++; 635186545Srwatson } 636186545Srwatson if (hard) { 637186545Srwatson if (warn_hard) 638186545Srwatson (*warn_hard)(cur_dir); 639186545Srwatson hcnt++; 640186545Srwatson } 641186545Srwatson dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent)); 642186545Srwatson if (dirent == NULL) 643186545Srwatson return (ADE_NOMEM); 644186545Srwatson dirent->softlim = soft; 645243750Srwatson dirent->hardlim = hard; 646186545Srwatson dirent->dirname = (char *) malloc(MAXNAMLEN); 647186545Srwatson if (dirent->dirname == NULL) { 648186545Srwatson free(dirent); 649186545Srwatson return (ADE_NOMEM); 650186545Srwatson } 651186545Srwatson strlcpy(dirent->dirname, cur_dir, MAXNAMLEN); 652186545Srwatson insert_orderly(dirent); 653186545Srwatson tcnt++; 654186545Srwatson } 655186545Srwatson 656186545Srwatson if (hcnt == tcnt) 657186545Srwatson return (ADE_HARDLIM); 658186545Srwatson if (scnt == tcnt) 659186545Srwatson return (ADE_SOFTLIM); 660186545Srwatson return (0); 661186545Srwatson} 662186545Srwatson 663186545Srwatsonvoid 664186545Srwatsonauditd_close_dirs(void) 665186545Srwatson{ 666186545Srwatson free_dir_q(); 667189279Srwatson auditd_minval = -1; 668189279Srwatson auditd_hostlen = -1; 669186545Srwatson} 670186545Srwatson 671186545Srwatson 672186545Srwatson/* 673186545Srwatson * Process the audit event file, obtaining a class mapping for each event, and 674186545Srwatson * set that mapping into the kernel. Return: 675243750Srwatson * n number of event mappings that were successfully processed, 676243750Srwatson * ADE_NOMEM if there was an error allocating memory. 677186545Srwatson */ 678186545Srwatsonint 679186545Srwatsonauditd_set_evcmap(void) 680186545Srwatson{ 681186545Srwatson au_event_ent_t ev, *evp; 682186545Srwatson au_evclass_map_t evc_map; 683186545Srwatson int ctr = 0; 684186545Srwatson 685186545Srwatson /* 686186545Srwatson * XXX There's a risk here that the BSM library will return NULL 687186545Srwatson * for an event when it can't properly map it to a class. In that 688186545Srwatson * case, we will not process any events beyond the one that failed, 689186545Srwatson * but should. We need a way to get a count of the events. 690186545Srwatson */ 691186545Srwatson ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX); 692186545Srwatson ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX); 693243750Srwatson if (ev.ae_name == NULL || ev.ae_desc == NULL) { 694186545Srwatson if (ev.ae_name != NULL) 695186545Srwatson free(ev.ae_name); 696186545Srwatson return (ADE_NOMEM); 697186545Srwatson } 698243750Srwatson 699186545Srwatson /* 700186545Srwatson * XXXRW: Currently we have no way to remove mappings from the kernel 701186545Srwatson * when they are removed from the file-based mappings. 702186545Srwatson */ 703186545Srwatson evp = &ev; 704186545Srwatson setauevent(); 705186545Srwatson while ((evp = getauevent_r(evp)) != NULL) { 706186545Srwatson evc_map.ec_number = evp->ae_number; 707186545Srwatson evc_map.ec_class = evp->ae_class; 708191273Srwatson if (audit_set_class(&evc_map, sizeof(evc_map)) == 0) 709186545Srwatson ctr++; 710186545Srwatson } 711186545Srwatson endauevent(); 712186545Srwatson free(ev.ae_name); 713186545Srwatson free(ev.ae_desc); 714186545Srwatson 715186545Srwatson return (ctr); 716186545Srwatson} 717186545Srwatson 718186545Srwatson/* 719186545Srwatson * Get the non-attributable event string and set the kernel mask. Return: 720243750Srwatson * ADE_NOERR on success, 721186545Srwatson * ADE_PARSE error parsing audit_control(5), 722186545Srwatson * ADE_AUDITON error setting the mask using auditon(2). 723186545Srwatson */ 724186545Srwatsonint 725186545Srwatsonauditd_set_namask(void) 726186545Srwatson{ 727186545Srwatson au_mask_t aumask; 728186545Srwatson char naeventstr[NA_EVENT_STR_SIZE]; 729243750Srwatson 730243750Srwatson if (getacna(naeventstr, NA_EVENT_STR_SIZE) != 0 || 731243750Srwatson getauditflagsbin(naeventstr, &aumask) != 0) 732186545Srwatson return (ADE_PARSE); 733186545Srwatson 734191273Srwatson if (audit_set_kmask(&aumask, sizeof(aumask)) != 0) 735186545Srwatson return (ADE_AUDITON); 736186545Srwatson 737186545Srwatson return (ADE_NOERR); 738186545Srwatson} 739186545Srwatson 740186545Srwatson/* 741186545Srwatson * Set the audit control policy if a policy is configured in audit_control(5), 742186545Srwatson * implement the policy. However, if one isn't defined or if there is an error 743186545Srwatson * parsing the control file, set AUDIT_CNT to avoid leaving the system in a 744186545Srwatson * fragile state. Return: 745243750Srwatson * ADE_NOERR on success, 746186545Srwatson * ADE_PARSE error parsing audit_control(5), 747186545Srwatson * ADE_AUDITON error setting policy using auditon(2). 748186545Srwatson */ 749186545Srwatsonint 750186545Srwatsonauditd_set_policy(void) 751186545Srwatson{ 752191273Srwatson int policy; 753186545Srwatson char polstr[POL_STR_SIZE]; 754186545Srwatson 755243750Srwatson if (getacpol(polstr, POL_STR_SIZE) != 0 || 756243750Srwatson au_strtopol(polstr, &policy) != 0) { 757186545Srwatson policy = AUDIT_CNT; 758191273Srwatson if (audit_set_policy(&policy) != 0) 759186545Srwatson return (ADE_AUDITON); 760186545Srwatson return (ADE_PARSE); 761243750Srwatson } 762186545Srwatson 763191273Srwatson if (audit_set_policy(&policy) != 0) 764186545Srwatson return (ADE_AUDITON); 765186545Srwatson 766186545Srwatson return (ADE_NOERR); 767186545Srwatson} 768186545Srwatson 769243750Srwatson/* 770186545Srwatson * Set trail rotation size. Return: 771243750Srwatson * ADE_NOERR on success, 772186545Srwatson * ADE_PARSE error parsing audit_control(5), 773186545Srwatson * ADE_AUDITON error setting file size using auditon(2). 774186545Srwatson */ 775186545Srwatsonint 776186545Srwatsonauditd_set_fsize(void) 777186545Srwatson{ 778186545Srwatson size_t filesz; 779186545Srwatson au_fstat_t au_fstat; 780186545Srwatson 781186545Srwatson /* 782186545Srwatson * Set trail rotation size. 783186545Srwatson */ 784186545Srwatson if (getacfilesz(&filesz) != 0) 785186545Srwatson return (ADE_PARSE); 786186545Srwatson 787186545Srwatson bzero(&au_fstat, sizeof(au_fstat)); 788186545Srwatson au_fstat.af_filesz = filesz; 789191273Srwatson if (audit_set_fsize(&au_fstat, sizeof(au_fstat)) != 0) 790186545Srwatson return (ADE_AUDITON); 791186545Srwatson 792243750Srwatson return (ADE_NOERR); 793186545Srwatson} 794186545Srwatson 795243750Srwatsonstatic void 796243750Srwatsoninject_dist(const char *fromname, char *toname, size_t tonamesize) 797243750Srwatson{ 798243750Srwatson char *ptr; 799243750Srwatson 800243750Srwatson ptr = strrchr(fromname, '/'); 801243750Srwatson assert(ptr != NULL); 802243750Srwatson assert(ptr - fromname < (ssize_t)tonamesize); 803243750Srwatson strlcpy(toname, fromname, ptr - fromname + 1); 804243750Srwatson strlcat(toname, "/dist/", tonamesize); 805243750Srwatson strlcat(toname, ptr + 1, tonamesize); 806243750Srwatson} 807243750Srwatson 808243750Srwatsonstatic int 809243750Srwatsonauditdist_link(const char *filename) 810243750Srwatson{ 811243750Srwatson char fname[MAXPATHLEN]; 812243750Srwatson 813243750Srwatson if (auditd_dist) { 814243750Srwatson inject_dist(filename, fname, sizeof(fname)); 815243750Srwatson /* Ignore errors. */ 816243750Srwatson (void) link(filename, fname); 817243750Srwatson } 818243750Srwatson 819243750Srwatson return (0); 820243750Srwatson} 821243750Srwatson 822243750Srwatsonint 823243750Srwatsonauditd_rename(const char *fromname, const char *toname) 824243750Srwatson{ 825243750Srwatson char fname[MAXPATHLEN], tname[MAXPATHLEN]; 826243750Srwatson 827243750Srwatson if (auditd_dist) { 828243750Srwatson inject_dist(fromname, fname, sizeof(fname)); 829243750Srwatson inject_dist(toname, tname, sizeof(tname)); 830243750Srwatson /* Ignore errors. */ 831243750Srwatson (void) rename(fname, tname); 832243750Srwatson } 833243750Srwatson 834243750Srwatson return (rename(fromname, toname)); 835243750Srwatson} 836243750Srwatson 837186545Srwatson/* 838243750Srwatson * Create the new audit file with appropriate permissions and ownership. 839243750Srwatson * Call auditctl(2) for this file. 840243750Srwatson * Try to clean up if something goes wrong. 841243750Srwatson * *errorp is modified only on auditctl(2) failure. 842186545Srwatson */ 843186545Srwatsonstatic int 844243750Srwatsonopen_trail(char *fname, gid_t gid, int *errorp) 845186545Srwatson{ 846243750Srwatson int fd; 847243750Srwatson 848243750Srwatson /* XXXPJD: What should we do if the file already exists? */ 849243750Srwatson fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR); 850186545Srwatson if (fd < 0) 851186545Srwatson return (-1); 852243750Srwatson if (fchown(fd, -1, gid) < 0 || fchmod(fd, S_IRUSR | S_IRGRP) < 0) { 853243750Srwatson (void) close(fd); 854243750Srwatson (void) unlink(fname); 855186545Srwatson return (-1); 856186545Srwatson } 857243750Srwatson (void) close(fd); 858243750Srwatson if (auditctl(fname) < 0) { 859243750Srwatson *errorp = errno; 860243750Srwatson (void) unlink(fname); 861243750Srwatson return (-1); 862243750Srwatson } 863243750Srwatson (void) auditdist_link(fname); 864243750Srwatson return (0); 865186545Srwatson} 866186545Srwatson 867186545Srwatson/* 868186545Srwatson * Create the new audit trail file, swap with existing audit file. Arguments 869186545Srwatson * include timestamp for the filename, a pointer to a string for returning the 870243750Srwatson * new file name, GID for trail file, and audit_warn function pointer for 871186545Srwatson * 'getacdir()' errors. Returns: 872243750Srwatson * ADE_NOERR on success, 873243750Srwatson * ADE_STRERR if the file name string could not be created, 874243750Srwatson * ADE_SWAPERR if the audit trail file could not be swapped, 875243750Srwatson * ADE_ACTL if the auditctl(2) call failed but file swap still 876186545Srwatson * successful. 877186545Srwatson * ADE_ACTLERR if the auditctl(2) call failed and file swap failed. 878186545Srwatson * ADE_SYMLINK if symlink(2) failed updating the current link. 879186545Srwatson */ 880186545Srwatsonint 881243750Srwatsonauditd_swap_trail(char *TS, char **newfile, gid_t gid, 882186545Srwatson int (*warn_getacdir)(char *)) 883186545Srwatson{ 884243750Srwatson char timestr[FILENAME_LEN + 1]; 885186545Srwatson char *fn; 886186545Srwatson struct dir_ent *dirent; 887186545Srwatson int saverrno = 0; 888243750Srwatson 889243750Srwatson if (strlen(TS) != TIMESTAMP_LEN || 890243750Srwatson snprintf(timestr, sizeof(timestr), "%s.%s", TS, 891243750Srwatson NOT_TERMINATED) < 0) { 892186545Srwatson errno = EINVAL; 893186545Srwatson return (ADE_STRERR); 894186545Srwatson } 895243750Srwatson 896186545Srwatson /* Try until we succeed. */ 897189279Srwatson TAILQ_FOREACH(dirent, &dir_q, dirs) { 898243750Srwatson if (dirent->hardlim) 899186545Srwatson continue; 900186545Srwatson if ((fn = affixdir(timestr, dirent)) == NULL) 901186545Srwatson return (ADE_STRERR); 902186545Srwatson 903186545Srwatson /* 904243750Srwatson * Create the file and pass to the kernel if all went well. 905186545Srwatson */ 906243750Srwatson if (open_trail(fn, gid, &saverrno) == 0) { 907243750Srwatson /* Success. */ 908243750Srwatson *newfile = fn; 909243750Srwatson if (saverrno) { 910243750Srwatson /* 911243750Srwatson * auditctl() failed but still 912243750Srwatson * successful. Return errno and "soft" 913243750Srwatson * error. 914186545Srwatson */ 915243750Srwatson errno = saverrno; 916243750Srwatson return (ADE_ACTL); 917243750Srwatson } 918243750Srwatson return (ADE_NOERR); 919243750Srwatson } 920186545Srwatson /* 921243750Srwatson * auditctl failed setting log file. Try again. 922243750Srwatson */ 923243750Srwatson /* 924186545Srwatson * Tell the administrator about lack of permissions for dir. 925186545Srwatson */ 926186545Srwatson if (warn_getacdir != NULL) 927186545Srwatson (*warn_getacdir)(dirent->dirname); 928186545Srwatson } 929186545Srwatson if (saverrno) { 930186545Srwatson errno = saverrno; 931186545Srwatson return (ADE_ACTLERR); 932186545Srwatson } else 933186545Srwatson return (ADE_SWAPERR); 934186545Srwatson} 935186545Srwatson 936186545Srwatson/* 937186545Srwatson * Mask calling process from being audited. Returns: 938186545Srwatson * ADE_NOERR on success, 939186545Srwatson * ADE_SETAUDIT if setaudit(2) fails. 940186545Srwatson */ 941189279Srwatson#ifdef __APPLE__ 942186545Srwatsonint 943186545Srwatsonauditd_prevent_audit(void) 944186545Srwatson{ 945189279Srwatson auditinfo_addr_t aia; 946189279Srwatson 947243750Srwatson /* 948189279Srwatson * To prevent event feedback cycles and avoid audit becoming stalled if 949189279Srwatson * auditing is suspended we mask this processes events from being 950189279Srwatson * audited. We allow the uid, tid, and mask fields to be implicitly 951243750Srwatson * set to zero, but do set the audit session ID to the PID. 952189279Srwatson * 953189279Srwatson * XXXRW: Is there more to it than this? 954189279Srwatson */ 955189279Srwatson bzero(&aia, sizeof(aia)); 956189279Srwatson aia.ai_asid = AU_ASSIGN_ASID; 957189279Srwatson aia.ai_termid.at_type = AU_IPv4; 958189279Srwatson if (setaudit_addr(&aia, sizeof(aia)) != 0) 959243750Srwatson return (ADE_SETAUDIT); 960189279Srwatson return (ADE_NOERR); 961189279Srwatson} 962189279Srwatson#else 963189279Srwatsonint 964189279Srwatsonauditd_prevent_audit(void) 965189279Srwatson{ 966186545Srwatson auditinfo_t ai; 967186545Srwatson 968243750Srwatson /* 969186545Srwatson * To prevent event feedback cycles and avoid audit becoming stalled if 970186545Srwatson * auditing is suspended we mask this processes events from being 971186545Srwatson * audited. We allow the uid, tid, and mask fields to be implicitly 972243750Srwatson * set to zero, but do set the audit session ID to the PID. 973186545Srwatson * 974186545Srwatson * XXXRW: Is there more to it than this? 975186545Srwatson */ 976186545Srwatson bzero(&ai, sizeof(ai)); 977186545Srwatson ai.ai_asid = getpid(); 978186545Srwatson if (setaudit(&ai) != 0) 979243750Srwatson return (ADE_SETAUDIT); 980186545Srwatson return (ADE_NOERR); 981186545Srwatson} 982243750Srwatson#endif /* !__APPLE__ */ 983186545Srwatson 984186545Srwatson/* 985186545Srwatson * Generate and submit audit record for audit startup or shutdown. The event 986186545Srwatson * argument can be AUE_audit_recovery, AUE_audit_startup or 987186545Srwatson * AUE_audit_shutdown. The path argument will add a path token, if not NULL. 988186545Srwatson * Returns: 989186545Srwatson * AUE_NOERR on success, 990186545Srwatson * ADE_NOMEM if memory allocation fails, 991243750Srwatson * ADE_AU_OPEN if au_open(3) fails, 992186545Srwatson * ADE_AU_CLOSE if au_close(3) fails. 993186545Srwatson */ 994186545Srwatsonint 995186545Srwatsonauditd_gen_record(int event, char *path) 996186545Srwatson{ 997186545Srwatson int aufd; 998186545Srwatson uid_t uid; 999186545Srwatson pid_t pid; 1000186545Srwatson char *autext = NULL; 1001186545Srwatson token_t *tok; 1002186545Srwatson struct auditinfo_addr aia; 1003186545Srwatson 1004186545Srwatson if (event == AUE_audit_startup) 1005186545Srwatson asprintf(&autext, "%s::Audit startup", getprogname()); 1006186545Srwatson else if (event == AUE_audit_shutdown) 1007186545Srwatson asprintf(&autext, "%s::Audit shutdown", getprogname()); 1008186545Srwatson else if (event == AUE_audit_recovery) 1009186545Srwatson asprintf(&autext, "%s::Audit recovery", getprogname()); 1010243750Srwatson else 1011186545Srwatson return (ADE_INVAL); 1012186545Srwatson if (autext == NULL) 1013186545Srwatson return (ADE_NOMEM); 1014186545Srwatson 1015186545Srwatson if ((aufd = au_open()) == -1) { 1016186545Srwatson free(autext); 1017186545Srwatson return (ADE_AU_OPEN); 1018186545Srwatson } 1019186545Srwatson bzero(&aia, sizeof(aia)); 1020186545Srwatson uid = getuid(); pid = getpid(); 1021186545Srwatson if ((tok = au_to_subject32_ex(uid, geteuid(), getegid(), uid, getgid(), 1022243750Srwatson pid, pid, &aia.ai_termid)) != NULL) 1023186545Srwatson au_write(aufd, tok); 1024186545Srwatson if ((tok = au_to_text(autext)) != NULL) 1025186545Srwatson au_write(aufd, tok); 1026186545Srwatson free(autext); 1027186545Srwatson if (path != NULL && (tok = au_to_path(path)) != NULL) 1028186545Srwatson au_write(aufd, tok); 1029186545Srwatson if ((tok = au_to_return32(0, 0)) != NULL) 1030186545Srwatson au_write(aufd, tok); 1031186545Srwatson if (au_close(aufd, 1, event) == -1) 1032186545Srwatson return (ADE_AU_CLOSE); 1033186545Srwatson 1034186545Srwatson return (ADE_NOERR); 1035186545Srwatson} 1036186545Srwatson 1037186545Srwatson/* 1038186545Srwatson * Check for a 'current' symlink and do crash recovery, if needed. Create a new 1039186545Srwatson * 'current' symlink. The argument 'curfile' is the file the 'current' symlink 1040186545Srwatson * should point to. Returns: 1041186545Srwatson * ADE_NOERR on success, 1042243750Srwatson * ADE_AU_OPEN if au_open(3) fails, 1043243750Srwatson * ADE_AU_CLOSE if au_close(3) fails. 1044186545Srwatson * ADE_RENAME if error renaming audit trail file, 1045186545Srwatson * ADE_READLINK if error reading the 'current' link, 1046186545Srwatson * ADE_SYMLINK if error creating 'current' link. 1047186545Srwatson */ 1048186545Srwatsonint 1049186545Srwatsonauditd_new_curlink(char *curfile) 1050186545Srwatson{ 1051186545Srwatson int len, err; 1052186545Srwatson char *ptr; 1053186545Srwatson char *path = NULL; 1054186545Srwatson struct stat sb; 1055186545Srwatson char recoveredname[MAXPATHLEN]; 1056186545Srwatson char newname[MAXPATHLEN]; 1057186545Srwatson 1058186545Srwatson /* 1059186545Srwatson * Check to see if audit was shutdown properly. If not, clean up, 1060186545Srwatson * recover previous audit trail file, and generate audit record. 1061186545Srwatson */ 1062243750Srwatson len = readlink(AUDIT_CURRENT_LINK, recoveredname, 1063243750Srwatson sizeof(recoveredname) - 1); 1064186545Srwatson if (len > 0) { 1065186545Srwatson /* 'current' exist but is it pointing at a valid file? */ 1066186545Srwatson recoveredname[len++] = '\0'; 1067243750Srwatson if (stat(recoveredname, &sb) == 0) { 1068186545Srwatson /* Yes, rename it to a crash recovery file. */ 1069243750Srwatson strlcpy(newname, recoveredname, sizeof(newname)); 1070186545Srwatson 1071186545Srwatson if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) { 1072189279Srwatson memcpy(ptr, CRASH_RECOVERY, POSTFIX_LEN); 1073243750Srwatson if (auditd_rename(recoveredname, newname) != 0) 1074186545Srwatson return (ADE_RENAME); 1075186545Srwatson } else 1076186545Srwatson return (ADE_STRERR); 1077186545Srwatson 1078186545Srwatson path = newname; 1079186545Srwatson } 1080186545Srwatson 1081186545Srwatson /* 'current' symlink is (now) invalid so remove it. */ 1082186545Srwatson (void) unlink(AUDIT_CURRENT_LINK); 1083186545Srwatson 1084186545Srwatson /* Note the crash recovery in current audit trail */ 1085186545Srwatson err = auditd_gen_record(AUE_audit_recovery, path); 1086186545Srwatson if (err) 1087186545Srwatson return (err); 1088186545Srwatson } 1089186545Srwatson 1090186545Srwatson if (len < 0 && errno != ENOENT) 1091186545Srwatson return (ADE_READLINK); 1092186545Srwatson 1093186545Srwatson if (symlink(curfile, AUDIT_CURRENT_LINK) != 0) 1094186545Srwatson return (ADE_SYMLINK); 1095186545Srwatson 1096186545Srwatson return (0); 1097186545Srwatson} 1098186545Srwatson 1099186545Srwatson/* 1100186545Srwatson * Do just what we need to quickly start auditing. Assume no system logging or 1101186545Srwatson * notify. Return: 1102186545Srwatson * 0 on success, 1103186545Srwatson * -1 on failure. 1104186545Srwatson */ 1105186545Srwatsonint 1106186545Srwatsonaudit_quick_start(void) 1107186545Srwatson{ 1108186545Srwatson int err; 1109189279Srwatson char *newfile = NULL; 1110186545Srwatson time_t tt; 1111243750Srwatson char TS[TIMESTAMP_LEN + 1]; 1112189279Srwatson int ret = 0; 1113186545Srwatson 1114243750Srwatson /* 1115186545Srwatson * Mask auditing of this process. 1116186545Srwatson */ 1117186545Srwatson if (auditd_prevent_audit() != 0) 1118186545Srwatson return (-1); 1119186545Srwatson 1120186545Srwatson /* 1121186545Srwatson * Read audit_control and get log directories. 1122186545Srwatson */ 1123243750Srwatson err = auditd_read_dirs(NULL, NULL); 1124186545Srwatson if (err != ADE_NOERR && err != ADE_SOFTLIM) 1125186545Srwatson return (-1); 1126186545Srwatson 1127186545Srwatson /* 1128243750Srwatson * Setup trail file distribution. 1129243750Srwatson */ 1130243750Srwatson (void) auditd_set_dist(); 1131243750Srwatson 1132243750Srwatson /* 1133186545Srwatson * Create a new audit trail log. 1134186545Srwatson */ 1135243750Srwatson if (getTSstr(tt, TS, sizeof(TS)) != 0) 1136186545Srwatson return (-1); 1137186545Srwatson err = auditd_swap_trail(TS, &newfile, getgid(), NULL); 1138189279Srwatson if (err != ADE_NOERR && err != ADE_ACTL) { 1139189279Srwatson ret = -1; 1140189279Srwatson goto out; 1141189279Srwatson } 1142186545Srwatson 1143186545Srwatson /* 1144243750Srwatson * Add the current symlink and recover from crash, if needed. 1145186545Srwatson */ 1146189279Srwatson if (auditd_new_curlink(newfile) != 0) { 1147189279Srwatson ret = -1; 1148189279Srwatson goto out; 1149189279Srwatson } 1150186545Srwatson 1151186545Srwatson /* 1152186545Srwatson * At this point auditing has started so generate audit start-up record. 1153186545Srwatson */ 1154189279Srwatson if (auditd_gen_record(AUE_audit_startup, NULL) != 0) { 1155189279Srwatson ret = -1; 1156189279Srwatson goto out; 1157189279Srwatson } 1158186545Srwatson 1159186545Srwatson /* 1160186545Srwatson * Configure the audit controls. 1161186545Srwatson */ 1162186545Srwatson (void) auditd_set_evcmap(); 1163186545Srwatson (void) auditd_set_namask(); 1164186545Srwatson (void) auditd_set_policy(); 1165186545Srwatson (void) auditd_set_fsize(); 1166186545Srwatson (void) auditd_set_minfree(); 1167186545Srwatson (void) auditd_set_host(); 1168186545Srwatson 1169189279Srwatsonout: 1170189279Srwatson if (newfile != NULL) 1171189279Srwatson free(newfile); 1172189279Srwatson 1173189279Srwatson return (ret); 1174186545Srwatson} 1175186545Srwatson 1176186545Srwatson/* 1177186545Srwatson * Shut down auditing quickly. Assumes that is only called on system shutdown. 1178186545Srwatson * Returns: 1179186545Srwatson * 0 on success, 1180186545Srwatson * -1 on failure. 1181186545Srwatson */ 1182186545Srwatsonint 1183186545Srwatsonaudit_quick_stop(void) 1184186545Srwatson{ 1185186545Srwatson int len; 1186191273Srwatson int cond; 1187186545Srwatson char *ptr; 1188186545Srwatson time_t tt; 1189186545Srwatson char oldname[MAXPATHLEN]; 1190186545Srwatson char newname[MAXPATHLEN]; 1191243750Srwatson char TS[TIMESTAMP_LEN + 1]; 1192186545Srwatson 1193186545Srwatson /* 1194186545Srwatson * Auditing already disabled? 1195186545Srwatson */ 1196191273Srwatson if (audit_get_cond(&cond) != 0) 1197186545Srwatson return (-1); 1198187214Srwatson if (cond == AUC_NOAUDIT) 1199186545Srwatson return (0); 1200186545Srwatson 1201186545Srwatson /* 1202186545Srwatson * Generate audit shutdown record. 1203186545Srwatson */ 1204186545Srwatson (void) auditd_gen_record(AUE_audit_shutdown, NULL); 1205186545Srwatson 1206186545Srwatson /* 1207186545Srwatson * Shutdown auditing in the kernel. 1208186545Srwatson */ 1209186545Srwatson cond = AUC_DISABLED; 1210191273Srwatson if (audit_set_cond(&cond) != 0) 1211186545Srwatson return (-1); 1212186545Srwatson#ifdef __BSM_INTERNAL_NOTIFY_KEY 1213186545Srwatson notify_post(__BSM_INTERNAL_NOTIFY_KEY); 1214186545Srwatson#endif 1215186545Srwatson 1216186545Srwatson /* 1217186545Srwatson * Rename last audit trail and remove 'current' link. 1218186545Srwatson */ 1219243750Srwatson len = readlink(AUDIT_CURRENT_LINK, oldname, sizeof(oldname) - 1); 1220186545Srwatson if (len < 0) 1221186545Srwatson return (-1); 1222186545Srwatson oldname[len++] = '\0'; 1223186545Srwatson 1224243750Srwatson if (getTSstr(tt, TS, sizeof(TS)) != 0) 1225186545Srwatson return (-1); 1226186545Srwatson 1227243750Srwatson strlcpy(newname, oldname, sizeof(newname)); 1228186545Srwatson 1229186545Srwatson if ((ptr = strstr(newname, NOT_TERMINATED)) != NULL) { 1230189279Srwatson memcpy(ptr, TS, POSTFIX_LEN); 1231243750Srwatson if (auditd_rename(oldname, newname) != 0) 1232186545Srwatson return (-1); 1233186545Srwatson } else 1234186545Srwatson return (-1); 1235243750Srwatson 1236186545Srwatson (void) unlink(AUDIT_CURRENT_LINK); 1237186545Srwatson 1238186545Srwatson return (0); 1239186545Srwatson} 1240