X-Git-Url: https://git.saurik.com/apple/syslog.git/blobdiff_plain/b16a592aee171655b85f8e45c0604c38494ab8ea..d98c3baa5cae23155bd982d95133aaaacd69e227:/syslogd.tproj/daemon.c?ds=sidebyside diff --git a/syslogd.tproj/daemon.c b/syslogd.tproj/daemon.c index 775a0d7..ba83182 100644 --- a/syslogd.tproj/daemon.c +++ b/syslogd.tproj/daemon.c @@ -1,23 +1,22 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.0 (the 'License'). You may not use this file - * except in compliance with the License. Please obtain a copy of the - * License at http://www.apple.com/publicsource and read it before using - * this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License." + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -30,439 +29,924 @@ #include #include #include -#include #include #include #define SYSLOG_NAMES #include +#include +#include +#include +#include +#include +#include +#include #include "daemon.h" +#define LIST_SIZE_DELTA 256 + #define streq(A,B) (strcmp(A,B)==0) +#define forever for(;;) + +#define ASL_MSG_TYPE_MASK 0x0000000f +#define ASL_TYPE_ERROR 2 + +#define ASL_KEY_FACILITY "Facility" + +#define FACILITY_USER "user" +#define FACILITY_CONSOLE "com.apple.console" +#define SYSTEM_RESERVED "com.apple.system" +#define SYSTEM_RESERVED_LEN 16 + +#define VERIFY_STATUS_OK 0 +#define VERIFY_STATUS_INVALID_MESSAGE 1 +#define VERIFY_STATUS_EXCEEDED_QUOTA 2 + +extern void disaster_message(aslmsg m); +extern int asl_action_reset(void); static char myname[MAXHOSTNAMELEN + 1] = {0}; -static int gotname = 0; +static int name_change_token = -1; -struct aslevent -{ - int fd; - unsigned char read:1; - unsigned char write:1; - unsigned char except:1; - aslreadfn readfn; - aslwritefn writefn; - aslexceptfn exceptfn; - char *sender; - uid_t uid; - gid_t gid; - TAILQ_ENTRY(aslevent) entries; -}; +static OSSpinLock count_lock = 0; -struct asloutput -{ - aslsendmsgfn sendmsg; - const char *outid; - TAILQ_ENTRY(asloutput) entries; -}; +#ifndef CONFIG_IPHONE +static vproc_transaction_t vproc_trans = {0}; +#endif -struct aslmatch +#define QUOTA_TABLE_SIZE 8192 +#define QUOTA_TABLE_SLOTS 8 + +#define QUOTA_EXCEEDED_MESSAGE "*** process %d exceeded %d log message per second limit - remaining messages this second discarded ***" +#define QUOTA_KERN_EXCEEDED_MESSAGE "*** kernel exceeded %d log message per second limit - remaining messages this second discarded ***" +#define QUOTA_EXCEEDED_LEVEL "3" + +#define DEFAULT_DB_FILE_MAX 25600000 +#define DEFAULT_DB_MEMORY_MAX 8192 +#define DEFAULT_DB_MINI_MAX 256 +#define DEFAULT_MPS_LIMIT 500 +#define DEFAULT_REMOTE_DELAY 5000 +#define DEFAULT_BSD_MAX_DUP_SEC 30 +#define DEFAULT_MARK_SEC 0 +#define DEFAULT_UTMP_TTL_SEC 31622400 + +static time_t quota_table_time = 0; +static pid_t quota_table_pid[QUOTA_TABLE_SIZE]; +static int32_t quota_table_quota[QUOTA_TABLE_SIZE]; +static int32_t kern_quota; +static int32_t kern_level; + +static const char *kern_notify_key[] = { - char *outid; - asl_msg_t *query; - TAILQ_ENTRY(aslmatch) entries; + "com.apple.system.log.kernel.emergency", + "com.apple.system.log.kernel.alert", + "com.apple.system.log.kernel.critical", + "com.apple.system.log.kernel.error", + "com.apple.system.log.kernel.warning", + "com.apple.system.log.kernel.notice", + "com.apple.system.log.kernel.info", + "com.apple.system.log.kernel.debug" }; -TAILQ_HEAD(ae, aslevent) Eventq; -TAILQ_HEAD(ao, asloutput) Outq; -TAILQ_HEAD(am, aslmatch) Matchq; +static int kern_notify_token[8] = {-1, -1, -1, -1, -1, -1, -1, -1 }; -int -aslevent_init(void) +static char ** +_insertString(char *s, char **l, uint32_t x) { - TAILQ_INIT(&Eventq); - TAILQ_INIT(&Outq); - TAILQ_INIT(&Matchq); + int i, len; - return 0; -} + if (s == NULL) return l; + if (l == NULL) + { + l = (char **)malloc(2 * sizeof(char *)); + if (l == NULL) return NULL; -int -aslevent_log(asl_msg_t *msg, char *outid) -{ - struct asloutput *i; - int status = -1; + l[0] = strdup(s); + if (l[0] == NULL) + { + free(l); + return NULL; + } + + l[1] = NULL; + return l; + } + + for (i = 0; l[i] != NULL; i++); + len = i + 1; /* count the NULL on the end of the list too! */ - for (i = Outq.tqh_first; i != NULL; i = i->entries.tqe_next) + l = (char **)reallocf(l, (len + 1) * sizeof(char *)); + if (l == NULL) return NULL; + + if ((x >= (len - 1)) || (x == IndexNull)) { - if ((outid != NULL) && (strcmp(i->outid, outid) == 0)) + l[len - 1] = strdup(s); + if (l[len - 1] == NULL) { - status = i->sendmsg(msg, outid); + free(l); + return NULL; } + + l[len] = NULL; + return l; } - return status; + for (i = len; i > x; i--) l[i] = l[i - 1]; + l[x] = strdup(s); + if (l[x] == NULL) return NULL; + + return l; } -int -aslevent_addmatch(asl_msg_t *query, char *outid) +char ** +explode(const char *s, const char *delim) { - struct aslmatch *tmp; + char **l = NULL; + const char *p; + char *t, quote; + int i, n; - if (query == NULL) return -1; - if (outid == NULL) return -1; + if (s == NULL) return NULL; - tmp = calloc(1, sizeof(struct aslmatch)); - if (tmp == NULL) return -1; - tmp->query = query; - tmp->outid = outid; - TAILQ_INSERT_TAIL(&Matchq, tmp, entries); + quote = '\0'; - return 0; + p = s; + while (p[0] != '\0') + { + /* scan forward */ + for (i = 0; p[i] != '\0'; i++) + { + if (quote == '\0') + { + /* not inside a quoted string: check for delimiters and quotes */ + if (strchr(delim, p[i]) != NULL) break; + else if (p[i] == '\'') quote = p[i]; + else if (p[i] == '"') quote = p[i]; + } + else + { + /* inside a quoted string - look for matching quote */ + if (p[i] == quote) quote = '\0'; + } + } + + n = i; + t = malloc(n + 1); + if (t == NULL) return NULL; + + for (i = 0; i < n; i++) t[i] = p[i]; + t[n] = '\0'; + l = _insertString(t, l, IndexNull); + free(t); + t = NULL; + if (p[i] == '\0') return l; + if (p[i + 1] == '\0') l = _insertString("", l, IndexNull); + p = p + i + 1; + } + + return l; } void -aslevent_match(asl_msg_t *msg) +freeList(char **l) { - struct aslmatch *i; + int i; - if (msg == NULL) return; + if (l == NULL) return; + for (i = 0; l[i] != NULL; i++) free(l[i]); + free(l); +} + +/* + * Quotas are maintained using a very fast fixed-size table. + * We hash into the pid table (quota_table_pid) using the last 10 + * bits of the pid, so the table has 1024 "buckets". The table is + * actually just an array with 8 entry slots (for collisions) per bucket. + * If there are more than 8 pids that hash to the same bucket, we + * re-use the one with the lowest message usage (highest remaining + * quota). This can lead to "generosity: if there are nine or more + * pids with the same last 10 bits all logging like crazy, we may + * end up allowing some of them to log more than their quota. + * That would be a remarkably rare occurrence. + */ + +static uint32_t +quota_check(pid_t pid, time_t now, aslmsg msg, uint32_t level) +{ + int i, x, maxx, max; + char *str, lstr[2]; + + if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE; + if (global.mps_limit == 0) return VERIFY_STATUS_OK; + + if (quota_table_time != now) + { + memset(quota_table_pid, 0, sizeof(quota_table_pid)); + kern_quota = global.mps_limit; + kern_level = 7; + quota_table_time = now; + } - for (i = Matchq.tqh_first; i != NULL; i = i->entries.tqe_next) + /* kernel gets it's own quota */ + if (pid == 0) { - if (asl_msg_cmp(i->query, msg) != 0) + if (level < kern_level) kern_level = level; + if (kern_quota > 0) kern_quota--; + + if (kern_quota > 0) return VERIFY_STATUS_OK; + if (kern_quota < 0) return VERIFY_STATUS_EXCEEDED_QUOTA; + + kern_quota = -1; + + str = NULL; + asprintf(&str, QUOTA_KERN_EXCEEDED_MESSAGE, global.mps_limit); + if (str != NULL) { - aslevent_log(msg, i->outid); + asl_set(msg, ASL_KEY_MSG, str); + free(str); + lstr[0] = kern_level + '0'; + lstr[1] = 0; + asl_set(msg, ASL_KEY_LEVEL, lstr); } + + return VERIFY_STATUS_OK; } -} -int -aslevent_removefd(int fd) -{ - struct aslevent *i; - int found = -1; + /* hash is last 10 bits of the pid, shifted up 3 bits to allow 8 slots per bucket */ + x = (pid & 0x000003ff) << 3; + maxx = x; + max = quota_table_quota[x]; - for (i = Eventq.tqh_first; i != NULL; i = i->entries.tqe_next) + for (i = 0; i < QUOTA_TABLE_SLOTS; i++) { - if (fd == i->fd) + if (quota_table_pid[x] == 0) { - asldebug("removing %d\n", i->fd); - TAILQ_REMOVE(&Eventq, i, entries); - found = 0; - if (i->sender != NULL) free(i->sender); - free(i); - i = NULL; - return 0; + quota_table_pid[x] = pid; + quota_table_quota[x] = global.mps_limit; + + return VERIFY_STATUS_OK; } - } - return -1; -} + if (quota_table_pid[x] == pid) + { + quota_table_quota[x] = quota_table_quota[x] - 1; -const char * -whatsmyhostname() -{ - char *dot; + if (quota_table_quota[x] == 0) + { + quota_table_quota[x] = -1; + + str = NULL; + asprintf(&str, QUOTA_EXCEEDED_MESSAGE, (int)pid, global.mps_limit); + if (str != NULL) + { + asl_set(msg, ASL_KEY_MSG, str); + free(str); + asl_set(msg, ASL_KEY_LEVEL, QUOTA_EXCEEDED_LEVEL); + } + + return VERIFY_STATUS_OK; + } - if (gotname != 0) return (const char *)myname; + if (quota_table_quota[x] < 0) + { + return VERIFY_STATUS_EXCEEDED_QUOTA; + } - if (gethostname(myname, MAXHOSTNAMELEN) < 0) - { - memset(myname, 0, sizeof(myname)); - return "localhost"; - } + return VERIFY_STATUS_OK; + } + + if (quota_table_quota[x] > max) + { + maxx = x; + max = quota_table_quota[x]; + } - if (strcmp(myname, "localhost")) gotname = 1; + x += 1; + } - dot = strchr(myname, '.'); - if (dot != NULL) *dot = '\0'; + /* can't find the pid and no slots were available - reuse slot with highest remaining quota */ + asldebug("Quotas: reused slot %d pid %d quota %d for new pid %d\n", maxx, (int)quota_table_pid[maxx], quota_table_quota[maxx], (int)pid); + quota_table_pid[maxx] = pid; + quota_table_quota[maxx] = global.mps_limit; - return (const char *)myname; + return VERIFY_STATUS_OK; } int -aslevent_addfd(int fd, aslreadfn readfn, aslwritefn writefn, aslexceptfn exceptfn) +asl_check_option(aslmsg msg, const char *opt) { - struct aslevent *e; - int found = 0; -#ifdef LOCAL_PEERCRED - struct xucred cr; -#endif - int len; - uid_t u; - gid_t g; - struct sockaddr_storage ss; - char *sender, str[256]; + const char *p; + uint32_t len; - u = 99; - g = 99; - sender = NULL; + if (msg == NULL) return 0; + if (opt == NULL) return 0; - memset(&ss, 0, sizeof(struct sockaddr_storage)); + len = strlen(opt); + if (len == 0) return 0; - len = sizeof(struct sockaddr_storage); + p = asl_get(msg, ASL_KEY_OPTION); + if (p == NULL) return 0; - if (getpeername(fd, (struct sockaddr *)&ss, &len) == 0) + while (*p != '\0') { - if (len == 0) - { - /* UNIX Domain socket */ - snprintf(str, sizeof(str), whatsmyhostname()); - sender = str; - } - else + while ((*p == ' ') || (*p == '\t') || (*p == ',')) p++; + if (*p == '\0') return 0; + + if (strncasecmp(p, opt, len) == 0) { - if (inet_ntop(ss.ss_family, (struct sockaddr *)&ss, str, 256) == 0) sender = str; + p += len; + if ((*p == ' ') || (*p == '\t') || (*p == ',') || (*p == '\0')) return 1; } + + while ((*p != ' ') && (*p != '\t') && (*p != ',') && (*p != '\0')) p++; } -#ifdef LOCAL_PEERCRED - len = sizeof(cr); + return 0; +} - if (getsockopt(fd, LOCAL_PEERCRED, 1, &cr, &len) == 0) - { - u = cr.cr_uid; - g = cr.cr_gid; - } -#endif +const char * +whatsmyhostname() +{ + static dispatch_once_t once; + char *dot; + int check, status; + + dispatch_once(&once, ^{ + snprintf(myname, sizeof(myname), "%s", "localhost"); + notify_register_check(kNotifySCHostNameChange, &name_change_token); + }); + + check = 1; + status = 0; + + if (name_change_token >= 0) status = notify_check(name_change_token, &check); - asldebug("fd %d UID %d GID %d Sender %s\n", fd, u, g, (sender == NULL) ? "NULL" : sender ); + if ((status == 0) && (check == 0)) return (const char *)myname; - for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) + if (gethostname(myname, MAXHOSTNAMELEN) < 0) { - if (fd == e->fd) - { - e->readfn = readfn; - e->writefn = writefn; - e->exceptfn = exceptfn; - if (e->sender != NULL) free(e->sender); - e->sender = NULL; - if (sender != NULL) e->sender = strdup(sender); - e->uid = u; - e->gid = g; - found = 1; - } + snprintf(myname, sizeof(myname), "%s", "localhost"); } + else + { + dot = strchr(myname, '.'); + if (dot != NULL) *dot = '\0'; + } + + return (const char *)myname; +} + +void +asl_client_count_increment() +{ + OSSpinLockLock(&count_lock); - if (found) return 0; +#ifndef CONFIG_IPHONE + if (global.client_count == 0) vproc_trans = vproc_transaction_begin(NULL); +#endif + global.client_count++; +#ifdef DEBUG + asldebug("global.client_count++ (%d)\n", global.client_count); +#endif - e = calloc(1, sizeof(struct aslevent)); - if (e == NULL) return -1; + OSSpinLockUnlock(&count_lock); +} - e->fd = fd; - e->readfn = readfn; - e->writefn = writefn; - e->exceptfn = exceptfn; - e->sender = NULL; - if (sender != NULL) e->sender = strdup(sender); - e->uid = u; - e->gid = g; +void +asl_client_count_decrement() +{ + OSSpinLockLock(&count_lock); - TAILQ_INSERT_TAIL(&Eventq, e, entries); + if (global.client_count > 0) global.client_count--; +#ifndef CONFIG_IPHONE + if (global.client_count == 0) vproc_transaction_end(NULL, vproc_trans); +#endif +#ifdef DEBUG + asldebug("global.client_count-- (%d)\n", global.client_count); +#endif - return 0; + OSSpinLockUnlock(&count_lock); } -int -aslmsg_verify(struct aslevent *e, asl_msg_t *msg) +/* + * Checks message content and sets attributes as required + * + * SOURCE_INTERNAL log messages sent by syslogd itself + * SOURCE_ASL_SOCKET legacy asl(3) TCP socket + * SOURCE_BSD_SOCKET legacy syslog(3) UDP socket + * SOURCE_UDP_SOCKET from the network + * SOURCE_KERN from the kernel + * SOURCE_ASL_MESSAGE mach messages sent from Libc by asl(3) and syslog(3) + * SOURCE_LAUNCHD forwarded from launchd + */ + +static uint32_t +aslmsg_verify(aslmsg msg, uint32_t source, int32_t *kern_post_level, uid_t *uid_out) { - const char *val; - char buf[32], *timestr; - time_t tick; - struct tm gtime; + const char *val, *fac, *ruval, *rgval; + char buf[64]; + time_t tick, now; + uid_t uid; + gid_t gid; + uint32_t status, level, fnum; + pid_t pid; + + if (msg == NULL) return VERIFY_STATUS_INVALID_MESSAGE; + + if (kern_post_level != NULL) *kern_post_level = -1; + if (uid_out != NULL) *uid_out = -2; + + /* PID */ + pid = 0; + + val = asl_get(msg, ASL_KEY_PID); + if (val == NULL) asl_set(msg, ASL_KEY_PID, "0"); + else pid = (pid_t)atoi(val); - if (msg == NULL) return -1; + /* if PID is 1 (launchd), use the refpid if there is one */ + if (pid == 1) + { + val = asl_get(msg, ASL_KEY_REF_PID); + if (val != NULL) pid = (pid_t)atoi(val); + } /* Time */ + now = time(NULL); + + /* Level */ + val = asl_get(msg, ASL_KEY_LEVEL); + level = ASL_LEVEL_DEBUG; + if ((val != NULL) && (val[1] == '\0') && (val[0] >= '0') && (val[0] <= '7')) level = val[0] - '0'; + snprintf(buf, sizeof(buf), "%d", level); + asl_set(msg, ASL_KEY_LEVEL, buf); + + /* + * check quota if no processes are watching + */ + if (global.watchers_active == 0) + { + status = quota_check(pid, now, msg, level); + if (status != VERIFY_STATUS_OK) return status; + } + tick = 0; val = asl_get(msg, ASL_KEY_TIME); if (val != NULL) tick = asl_parse_time(val); - if (tick == 0) tick = time(NULL); - memset(>ime, 0, sizeof(struct tm)); - gmtime_r(&tick, >ime); + /* Set time to now if it is unset or from the future (not allowed!) */ + if ((tick == 0) || (tick > now)) tick = now; - /* Canonical form: YYYY.MM.DD hh:mm:ss UTC */ - asprintf(×tr, "%d.%02d.%02d %02d:%02d:%02d UTC", gtime.tm_year + 1900, gtime.tm_mon + 1, gtime.tm_mday, gtime.tm_hour, gtime.tm_min, gtime.tm_sec); - if (timestr != NULL) + /* Canonical form: seconds since the epoch */ + snprintf(buf, sizeof(buf) - 1, "%lu", tick); + asl_set(msg, ASL_KEY_TIME, buf); + + val = asl_get(msg, ASL_KEY_HOST); + if (val == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); + + uid = -2; + val = asl_get(msg, ASL_KEY_UID); + if (val != NULL) { - asl_set(msg, ASL_KEY_TIME, timestr); - free(timestr); + uid = atoi(val); + if ((uid == 0) && strcmp(val, "0")) uid = -2; + if (uid_out != NULL) *uid_out = uid; } - /* Host */ - if (e == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); - else if (e->sender != NULL) asl_set(msg, ASL_KEY_HOST, e->sender); + gid = -2; + val = asl_get(msg, ASL_KEY_GID); + if (val != NULL) + { + gid = atoi(val); + if ((gid == 0) && strcmp(val, "0")) gid = -2; + } + + /* UID & GID */ + switch (source) + { + case SOURCE_KERN: + case SOURCE_INTERNAL: + { + asl_set(msg, ASL_KEY_UID, "0"); + asl_set(msg, ASL_KEY_GID, "0"); + break; + } + case SOURCE_ASL_SOCKET: + case SOURCE_ASL_MESSAGE: + case SOURCE_LAUNCHD: + { + /* we trust the UID & GID in the message */ + break; + } + default: + { + /* we do not trust the UID 0 or GID 0 or 80 in the message */ + if (uid == 0) asl_set(msg, ASL_KEY_UID, "-2"); + if ((gid == 0) || (gid == 80)) asl_set(msg, ASL_KEY_GID, "-2"); + } + } /* Sender */ val = asl_get(msg, ASL_KEY_SENDER); - if (val == NULL) asl_set(msg, ASL_KEY_SENDER, "Unknown"); - - /* PID */ - val = asl_get(msg, ASL_KEY_PID); - if (val == NULL) asl_set(msg, ASL_KEY_PID, "0"); - - /* UID */ - val = asl_get(msg, ASL_KEY_UID); if (val == NULL) { - if (e == NULL) asl_set(msg, ASL_KEY_UID, "-2"); - else if (e->uid == 99) asl_set(msg, ASL_KEY_UID, "-2"); - else + switch (source) { - snprintf(buf, sizeof(buf), "%d", e->uid); - asl_set(msg, ASL_KEY_UID, buf); + case SOURCE_KERN: + { + asl_set(msg, ASL_KEY_SENDER, "kernel"); + break; + } + case SOURCE_INTERNAL: + { + asl_set(msg, ASL_KEY_SENDER, "syslogd"); + break; + } + default: + { + asl_set(msg, ASL_KEY_SENDER, "Unknown"); + } } } - else if ((e != NULL) && (e->uid != 99)) + else if ((source != SOURCE_KERN) && (uid != 0) && (!strcmp(val, "kernel"))) { - snprintf(buf, sizeof(buf), "%d", e->uid); - asl_set(msg, ASL_KEY_UID, buf); + /* allow UID 0 to send messages with "Sender kernel", but nobody else */ + asl_set(msg, ASL_KEY_SENDER, "Unknown"); } - /* GID */ - val = asl_get(msg, ASL_KEY_GID); - if (val == NULL) + /* Facility */ + fac = asl_get(msg, ASL_KEY_FACILITY); + if (fac == NULL) { - if (e == NULL) asl_set(msg, ASL_KEY_GID, "-2"); - else if (e->gid == 99) asl_set(msg, ASL_KEY_GID, "-2"); - else + if (source == SOURCE_KERN) fac = "kern"; + else fac = "user"; + asl_set(msg, ASL_KEY_FACILITY, fac); + } + else if (fac[0] == '#') + { + fnum = LOG_USER; + if ((fac[1] >= '0') && (fac[1] <= '9')) { - snprintf(buf, sizeof(buf), "%d", e->gid); - asl_set(msg, ASL_KEY_GID, buf); + fnum = atoi(fac + 1) << 3; + if ((fnum == 0) && (strcmp(fac + 1, "0"))) fnum = LOG_USER; } + + fac = asl_syslog_faciliy_num_to_name(fnum); + asl_set(msg, ASL_KEY_FACILITY, fac); } - else if ((e != NULL) && (e->gid != 99)) + else if (!strncmp(fac, SYSTEM_RESERVED, SYSTEM_RESERVED_LEN)) { - snprintf(buf, sizeof(buf), "%d", e->gid); - asl_set(msg, ASL_KEY_GID, buf); + /* only UID 0 may use "com.apple.system" */ + if (uid != 0) asl_set(msg, ASL_KEY_FACILITY, FACILITY_USER); } - /* Level */ - val = asl_get(msg, ASL_KEY_LEVEL); - if (val == NULL) asl_set(msg, ASL_KEY_LEVEL, "7"); + /* + * kernel messages are only readable by root and admin group. + * all other messages are admin-only readable unless they already + * have specific read access controls set. + */ + if (source == SOURCE_KERN) + { + asl_set(msg, ASL_KEY_READ_UID, "0"); + asl_set(msg, ASL_KEY_READ_GID, "80"); + } + else + { + ruval = asl_get(msg, ASL_KEY_READ_UID); + rgval = asl_get(msg, ASL_KEY_READ_GID); - return 0; + if ((ruval == NULL) && (rgval == NULL)) + { + asl_set(msg, ASL_KEY_READ_GID, "80"); + } + } + + /* Set DB Expire Time for com.apple.system.utmpx and lastlog */ + if ((!strcmp(fac, "com.apple.system.utmpx")) || (!strcmp(fac, "com.apple.system.lastlog"))) + { + snprintf(buf, sizeof(buf), "%lu", tick + global.utmp_ttl); + asl_set(msg, ASL_KEY_EXPIRE_TIME, buf); + } + + /* Set DB Expire Time for Filesystem errors */ + if (!strcmp(fac, FSLOG_VAL_FACILITY)) + { + snprintf(buf, sizeof(buf), "%lu", tick + FS_TTL_SEC); + asl_set(msg, ASL_KEY_EXPIRE_TIME, buf); + } + + /* + * special case handling of kernel disaster messages + */ + if ((source == SOURCE_KERN) && (level <= KERN_DISASTER_LEVEL)) + { + if (kern_post_level != NULL) *kern_post_level = level; + disaster_message(msg); + } + + return VERIFY_STATUS_OK; } -int -aslevent_addoutput(aslsendmsgfn fn, const char *outid) +void +list_append_msg(asl_search_result_t *list, aslmsg msg) { - struct asloutput *tmp; + if (list == NULL) return; + if (msg == NULL) return; + + /* + * NB: curr is the list size + * grow list if necessary + */ + if (list->count == list->curr) + { + if (list->curr == 0) + { + list->msg = (asl_msg_t **)calloc(LIST_SIZE_DELTA, sizeof(asl_msg_t *)); + } + else + { + list->msg = (asl_msg_t **)reallocf(list->msg, (list->curr + LIST_SIZE_DELTA) * sizeof(asl_msg_t *)); + } - tmp = calloc(1, sizeof(struct asloutput)); - if (tmp == NULL) return -1; + if (list->msg == NULL) + { + list->curr = 0; + list->count = 0; + return; + } - tmp->sendmsg = fn; - tmp->outid = outid; + list->curr += LIST_SIZE_DELTA; + } - TAILQ_INSERT_TAIL(&Outq, tmp, entries); + list->msg[list->count] = (asl_msg_t *)msg; + list->count++; +} - return 0; +void +init_globals(void) +{ + OSSpinLockLock(&global.lock); + + global.debug = 0; + free(global.debug_file); + global.debug_file = NULL; + +#ifdef CONFIG_IPHONE + global.dbtype = DB_TYPE_MINI; +#else + global.dbtype = DB_TYPE_FILE; +#endif + global.db_file_max = DEFAULT_DB_FILE_MAX; + global.db_memory_max = DEFAULT_DB_MEMORY_MAX; + global.db_mini_max = DEFAULT_DB_MINI_MAX; + global.mps_limit = DEFAULT_MPS_LIMIT; + global.remote_delay_time = DEFAULT_REMOTE_DELAY; + global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC; + global.mark_time = DEFAULT_MARK_SEC; + global.utmp_ttl = DEFAULT_UTMP_TTL_SEC; + + OSSpinLockUnlock(&global.lock); } +/* + * Used to set config parameters. + * Line format "= name value" + */ int -aslevent_fdsets(fd_set *rd, fd_set *wr, fd_set *ex) +control_set_param(const char *s) { - struct aslevent *e; - int status = 0; + char **l; + uint32_t intval, count, v32a, v32b, v32c; - asldebug("--> aslevent_fdsets\n"); - FD_ZERO(rd); - FD_ZERO(wr); - FD_ZERO(ex); + if (s == NULL) return -1; + if (s[0] == '\0') return 0; - for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) + /* skip '=' and whitespace */ + s++; + while ((*s == ' ') || (*s == '\t')) s++; + + l = explode(s, " \t"); + if (l == NULL) return -1; + + for (count = 0; l[count] != NULL; count++); + + /* name is required */ + if (count == 0) + { + freeList(l); + return -1; + } + + /* value is required */ + if (count == 1) + { + freeList(l); + return -1; + } + + if (!strcasecmp(l[0], "debug")) + { + /* = debug {0|1} [file] */ + intval = atoi(l[1]); + config_debug(intval, l[2]); + } + else if (!strcasecmp(l[0], "mark_time")) + { + /* = mark_time seconds */ + OSSpinLockLock(&global.lock); + global.mark_time = atoll(l[1]); + OSSpinLockUnlock(&global.lock); + } + else if (!strcasecmp(l[0], "dup_delay")) + { + /* = bsd_max_dup_time seconds */ + OSSpinLockLock(&global.lock); + global.bsd_max_dup_time = atoll(l[1]); + OSSpinLockUnlock(&global.lock); + } + else if (!strcasecmp(l[0], "remote_delay")) + { + /* = remote_delay microseconds */ + OSSpinLockLock(&global.lock); + global.remote_delay_time = atol(l[1]); + OSSpinLockUnlock(&global.lock); + } + else if (!strcasecmp(l[0], "utmp_ttl")) + { + /* = utmp_ttl seconds */ + OSSpinLockLock(&global.lock); + global.utmp_ttl = (time_t)atoll(l[1]); + OSSpinLockUnlock(&global.lock); + } + else if (!strcasecmp(l[0], "mps_limit")) { - asldebug("adding fd %d\n", e->fd); - if (e->readfn) + /* = mps_limit number */ + OSSpinLockLock(&global.lock); + global.mps_limit = (uint32_t)atol(l[1]); + OSSpinLockUnlock(&global.lock); + } + else if (!strcasecmp(l[0], "max_file_size")) + { + /* = max_file_size bytes */ + pthread_mutex_lock(global.db_lock); + + if (global.dbtype & DB_TYPE_FILE) { - FD_SET(e->fd, rd); - status = MAX(e->fd, status); + asl_store_close(global.file_db); + global.file_db = NULL; + global.db_file_max = atoi(l[1]); } - if (e->writefn) + pthread_mutex_unlock(global.db_lock); + } + else if ((!strcasecmp(l[0], "db")) || (!strcasecmp(l[0], "database")) || (!strcasecmp(l[0], "datastore"))) + { + /* NB this is private / unpublished */ + /* = db type [max]... */ + + v32a = 0; + v32b = 0; + v32c = 0; + + if ((l[1][0] >= '0') && (l[1][0] <= '9')) { - FD_SET(e->fd, wr); - status = MAX(e->fd, status); + intval = atoi(l[1]); + if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]); + if ((count >= 4) && (strcmp(l[3], "-"))) v32b = atoi(l[3]); + if ((count >= 5) && (strcmp(l[4], "-"))) v32c = atoi(l[4]); } - - if (e->exceptfn) + else if (!strcasecmp(l[1], "file")) + { + intval = DB_TYPE_FILE; + if ((count >= 3) && (strcmp(l[2], "-"))) v32a = atoi(l[2]); + } + else if (!strncasecmp(l[1], "mem", 3)) + { + intval = DB_TYPE_MEMORY; + if ((count >= 3) && (strcmp(l[2], "-"))) v32b = atoi(l[2]); + } + else if (!strncasecmp(l[1], "min", 3)) + { + intval = DB_TYPE_MINI; + if ((count >= 3) && (strcmp(l[2], "-"))) v32c = atoi(l[2]); + } + else { - FD_SET(e->fd, ex); - status = MAX(e->fd, status); + freeList(l); + return -1; } + + if (v32a == 0) v32a = global.db_file_max; + if (v32b == 0) v32b = global.db_memory_max; + if (v32c == 0) v32c = global.db_mini_max; + + config_data_store(intval, v32a, v32b, v32c); + } + + freeList(l); + return 0; +} + +static int +control_message(aslmsg msg) +{ + const char *str = asl_get(msg, ASL_KEY_MSG); + + if (str == NULL) return 0; + + if (!strncmp(str, "= reset", 7)) + { + init_globals(); + return asl_action_reset(); + } + else if (!strncmp(str, "= rotate", 8)) + { + const char *p = str + 8; + while ((*p == ' ') || (*p == '\t')) p++; + if (*p == '\0') p = NULL; + return asl_action_file_rotate(p); + } + else if (!strncmp(str, "= ", 2)) + { + return control_set_param(str); } - asldebug("<--aslevent_fdsets\n"); - return status; + return 0; } void -aslevent_handleevent(fd_set rd, fd_set wr, fd_set ex, char *errstr) +process_message(aslmsg msg, uint32_t source) { - struct aslevent *e; - char *out = NULL; - asl_msg_t *msg; + int32_t kplevel; + uint32_t status; + uid_t uid; + + if (msg == NULL) return; - asldebug("--> aslevent_handleevent\n"); - if (errstr) errstr = NULL; + kplevel = -1; + uid = -2; - for (e = Eventq.tqh_first; e != NULL; e = e->entries.tqe_next) + status = aslmsg_verify(msg, source, &kplevel, &uid); + if (status == VERIFY_STATUS_OK) { - if (FD_ISSET(e->fd, &rd) && (e->readfn != NULL)) + if ((source == SOURCE_KERN) && (kplevel >= 0)) { - asldebug("handling read event on %d\n", e->fd); - msg = e->readfn(e->fd); - if (msg == NULL) + if (kplevel > 7) kplevel = 7; + if (kern_notify_token[kplevel] < 0) { - asldebug("error reading message\n"); - continue; + status = notify_register_plain(kern_notify_key[kplevel], &(kern_notify_token[kplevel])); + if (status != 0) asldebug("notify_register_plain(%s) failed status %u\n", status); } - if (aslmsg_verify(e, msg) < 0) - { - asl_free(msg); - asldebug("recieved invalid message\n"); - } - else - { - aslevent_match(msg); - asl_free(msg); - } + notify_post(kern_notify_key[kplevel]); } - if (FD_ISSET(e->fd, &ex) && e->exceptfn) - { - asldebug("handling except event on %d\n", e->fd); - out = e->exceptfn(e->fd); - if (out == NULL) asldebug("error writing message\n"); - } + if ((uid == 0) && asl_check_option(msg, ASL_OPT_CONTROL)) control_message(msg); + + /* send message to output modules */ + asl_out_message(msg); + if (global.bsd_out_enabled) bsd_out_message(msg); } - asldebug("<-- aslevent_handleevent\n"); + asl_free(msg); } int -asl_log_string(const char *str) +internal_log_message(const char *str) { - asl_msg_t *msg; + aslmsg msg; if (str == NULL) return 1; - msg = asl_msg_from_string(str); - if (aslmsg_verify(NULL, msg) < 0) - { - asl_free(msg); - return -1; - } + msg = (aslmsg)asl_msg_from_string(str); + if (msg == NULL) return 1; + + dispatch_async(global.work_queue, ^{ process_message(msg, SOURCE_INTERNAL); }); + + return 0; +} + +int +asldebug(const char *str, ...) +{ + va_list v; + FILE *dfp = NULL; + + if (global.debug == 0) return 0; + + if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a"); + else dfp = fopen(global.debug_file, "a"); + if (dfp == NULL) return 0; + + va_start(v, str); + vfprintf(dfp, str, v); + va_end(v); + + fclose(dfp); - aslevent_match(msg); - asl_free(msg); return 0; } void -aslmark(void) +asl_mark(void) { char *str; @@ -473,28 +957,27 @@ aslmark(void) ASL_KEY_PID, getpid(), ASL_KEY_MSG, ASL_KEY_UID, ASL_KEY_GID); - asl_log_string(str); + internal_log_message(str); if (str != NULL) free(str); } -asl_msg_t * -asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) +aslmsg +asl_syslog_input_convert(const char *in, int len, char *rhost, uint32_t source) { int pf, pri, index, n; - char *p, *colon, *brace, *tmp, *tval, *sval, *pval, *mval; + char *p, *colon, *brace, *space, *tmp, *tval, *hval, *sval, *pval, *mval; char prival[8]; const char *fval; - asl_msg_t *msg; + aslmsg msg; struct tm time; time_t tick; if (in == NULL) return NULL; if (len <= 0) return NULL; - asldebug("asl_syslog_input_convert: %s\n", in); - pri = LOG_DEBUG; tval = NULL; + hval = NULL; sval = NULL; pval = NULL; mval = NULL; @@ -503,6 +986,7 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) index = 0; p = (char *)in; + /* skip leading whitespace */ while ((index < len) && ((*p == ' ') || (*p == '\t'))) { p++; @@ -511,6 +995,7 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (index >= len) return NULL; + /* parse "" priority (level and facility) */ if (*p == '<') { p++; @@ -538,9 +1023,12 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) snprintf(prival, sizeof(prival), "%d", pri); + /* check if a timestamp is included */ if (((len - index) > 15) && (p[9] == ':') && (p[12] == ':') && (p[15] == ' ')) { tmp = malloc(16); + if (tmp == NULL) return NULL; + memcpy(tmp, p, 15); tmp[15] = '\0'; @@ -560,49 +1048,56 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) index += 16; } - if (kern != 0) + /* stop here for kernel messages */ + if (source == SOURCE_KERN) { - msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t)); - msg->type = ASL_TYPE_MSG; - - asl_set(msg, ASL_KEY_SENDER, "kernel"); - - asl_set(msg, "Facility", "kern"); - if (tval != NULL) - { - asl_set(msg, ASL_KEY_TIME, tval); - free(tval); - } + msg = asl_new(ASL_TYPE_MSG); + if (msg == NULL) return NULL; asl_set(msg, ASL_KEY_MSG, p); - asl_set(msg, ASL_KEY_LEVEL, prival); - asl_set(msg, ASL_KEY_PID, "0"); - asl_set(msg, ASL_KEY_UID, "0"); + return msg; + } - asl_set(msg, ASL_KEY_GID, "0"); + /* if message is from a network socket, hostname follows */ + if (source == SOURCE_UDP_SOCKET) + { + space = strchr(p, ' '); + if (space != NULL) + { + n = space - p; + hval = malloc(n + 1); + if (hval == NULL) return NULL; - asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); + memcpy(hval, p, n); + hval[n] = '\0'; - return msg; + p = space + 1; + index += (n + 1); + } } colon = strchr(p, ':'); brace = strchr(p, '['); + /* check for "sender:" or sender[pid]:" */ if (colon != NULL) { if ((brace != NULL) && (brace < colon)) { n = brace - p; sval = malloc(n + 1); + if (sval == NULL) return NULL; + memcpy(sval, p, n); sval[n] = '\0'; n = colon - (brace + 1) - 1; pval = malloc(n + 1); + if (pval == NULL) return NULL; + memcpy(pval, (brace + 1), n); pval[n] = '\0'; } @@ -610,6 +1105,8 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) { n = colon - p; sval = malloc(n + 1); + if (sval == NULL) return NULL; + memcpy(sval, p, n); sval[n] = '\0'; } @@ -629,14 +1126,16 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) if (n > 0) { mval = malloc(n + 1); + if (mval == NULL) return NULL; + memcpy(mval, p, n); mval[n] = '\0'; } if (fval == NULL) fval = asl_syslog_faciliy_num_to_name(LOG_USER); - msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t)); - msg->type = ASL_TYPE_MSG; + msg = asl_new(ASL_TYPE_MSG); + if (msg == NULL) return NULL; if (tval != NULL) { @@ -658,7 +1157,10 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) asl_set(msg, ASL_KEY_PID, pval); free(pval); } - else asl_set(msg, ASL_KEY_PID, "-1"); + else + { + asl_set(msg, ASL_KEY_PID, "-1"); + } if (mval != NULL) { @@ -670,15 +1172,57 @@ asl_syslog_input_convert(const char *in, int len, char *rhost, int kern) asl_set(msg, ASL_KEY_UID, "-2"); asl_set(msg, ASL_KEY_GID, "-2"); - if (rhost == NULL) asl_set(msg, ASL_KEY_HOST, whatsmyhostname()); - else asl_set(msg, ASL_KEY_HOST, rhost); + if (hval != NULL) + { + asl_set(msg, ASL_KEY_HOST, hval); + free(hval); + } + else if (rhost != NULL) + { + asl_set(msg, ASL_KEY_HOST, rhost); + } + + return msg; +} + +aslmsg +asl_input_parse(const char *in, int len, char *rhost, uint32_t source) +{ + aslmsg msg; + int status, x, legacy, off; + + asldebug("asl_input_parse: %s\n", (in == NULL) ? "NULL" : in); + + if (in == NULL) return NULL; + + legacy = 1; + msg = NULL; + + /* calculate length if not provided */ + if (len == 0) len = strlen(in); - if (msg->count == 0) + /* + * Determine if the input is "old" syslog format or new ASL format. + * Old format lines should start with "<", but they can just be straight text. + * ASL input may start with a length (10 bytes) followed by a space and a '['. + * The length is optional, so ASL messages may also just start with '['. + */ + if ((in[0] != '<') && (len > 11)) { - asl_free(msg); - return NULL; + status = sscanf(in, "%d ", &x); + if ((status == 1) && (in[10] == ' ') && (in[11] == '[')) legacy = 0; } + if (legacy == 1) return asl_syslog_input_convert(in, len, rhost, source); + + off = 11; + if (in[0] == '[') off = 0; + + msg = (aslmsg)asl_msg_from_string(in + off); + if (msg == NULL) return NULL; + + if (rhost != NULL) asl_set(msg, ASL_KEY_HOST, rhost); + return msg; } @@ -693,8 +1237,112 @@ get_line_from_file(FILE *f) if (len == 0) return NULL; s = malloc(len + 1); + if (s == NULL) return NULL; + memcpy(s, out, len); + if (s[len - 1] != '\n') len++; s[len - 1] = '\0'; return s; } + +void +launchd_callback(struct timeval *when, pid_t from_pid, pid_t about_pid, uid_t sender_uid, gid_t sender_gid, int priority, const char *from_name, const char *about_name, const char *session_name, const char *msg) +{ + aslmsg m; + char str[256]; + time_t now; + +/* + asldebug("launchd_callback Time %lu %lu PID %u RefPID %u UID %d GID %d PRI %d Sender %s Ref %s Session %s Message %s\n", + when->tv_sec, when->tv_usec, from_pid, about_pid, sender_uid, sender_gid, priority, from_name, about_name, session_name, msg); +*/ + + m = asl_new(ASL_TYPE_MSG); + if (m == NULL) return; + + /* Level */ + if (priority < ASL_LEVEL_EMERG) priority = ASL_LEVEL_EMERG; + if (priority > ASL_LEVEL_DEBUG) priority = ASL_LEVEL_DEBUG; + snprintf(str, sizeof(str), "%d", priority); + + asl_set(m, ASL_KEY_LEVEL, str); + + /* Time */ + if (when != NULL) + { + snprintf(str, sizeof(str), "%lu", when->tv_sec); + asl_set(m, ASL_KEY_TIME, str); + + snprintf(str, sizeof(str), "%lu", 1000 * (unsigned long int)when->tv_usec); + asl_set(m, ASL_KEY_TIME_NSEC, str); + } + else + { + now = time(NULL); + snprintf(str, sizeof(str), "%lu", now); + asl_set(m, ASL_KEY_TIME, str); + } + + /* Facility */ + asl_set(m, ASL_KEY_FACILITY, FACILITY_CONSOLE); + + /* UID */ + snprintf(str, sizeof(str), "%u", (unsigned int)sender_uid); + asl_set(m, ASL_KEY_UID, str); + + /* GID */ + snprintf(str, sizeof(str), "%u", (unsigned int)sender_gid); + asl_set(m, ASL_KEY_GID, str); + + /* PID */ + if (from_pid != 0) + { + snprintf(str, sizeof(str), "%u", (unsigned int)from_pid); + asl_set(m, ASL_KEY_PID, str); + } + + /* Reference PID */ + if ((about_pid > 0) && (about_pid != from_pid)) + { + snprintf(str, sizeof(str), "%u", (unsigned int)about_pid); + asl_set(m, ASL_KEY_REF_PID, str); + } + + /* Sender */ + if (from_name != NULL) + { + asl_set(m, ASL_KEY_SENDER, from_name); + } + + /* ReadUID */ + if (sender_uid != 0) + { + snprintf(str, sizeof(str), "%d", (int)sender_uid); + asl_set(m, ASL_KEY_READ_UID, str); + } + + /* Reference Process */ + if (about_name != NULL) + { + if ((from_name != NULL) && (strcmp(from_name, about_name) != 0)) + { + asl_set(m, ASL_KEY_REF_PROC, about_name); + } + } + + /* Session */ + if (session_name != NULL) + { + asl_set(m, ASL_KEY_SESSION, session_name); + } + + /* Message */ + if (msg != NULL) + { + asl_set(m, ASL_KEY_MSG, msg); + } + + dispatch_async(global.work_queue, ^{ process_message(m, SOURCE_LAUNCHD); }); +} +