#include <xpc/xpc.h>
#include <TargetConditionals.h>
#include <configuration_profile.h>
+#include <asl.h>
#include <asl_core.h>
#include <asl_msg.h>
#include "asl_common.h"
#define _PATH_ASL_CONF "/etc/asl.conf"
#define _PATH_ASL_CONF_DIR "/etc/asl"
+#define PATH_VAR_LOG "/var/log/"
+#define PATH_VAR_LOG_LEN 9
+
+#define PATH_LIBRARY_LOGS "/Library/Logs/"
+#define PATH_LIBRARY_LOGS_LEN 14
+
#if !TARGET_IPHONE_SIMULATOR
#define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
#endif
"notify ",
"broadcast ",
"access ",
+ "set ",
+ "unset ",
"store ",
"asl_file ",
"asl_dir ",
"set (profile)"
};
-static time_t start_today;
-
-extern asl_msg_t *asl_msg_from_string(const char *buf);
-
#define forever for(;;)
#define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
+#define STAMP_STYLE_INVALID -1
+#define STAMP_STYLE_NULL 0
+#define STAMP_STYLE_SEC 1
+#define STAMP_STYLE_SEQ 2
+#define STAMP_STYLE_UTC_OR_LCL 3
+
asl_msg_t *
xpc_object_to_asl_msg(xpc_object_t xobj)
{
return out;
}
+static asl_out_dst_data_t *
+_asl_out_dest_for_path(asl_out_module_t *m, const char *path)
+{
+ if (m == NULL) return NULL;
+ if (path == NULL) return NULL;
+
+ while (m != NULL)
+ {
+ asl_out_rule_t *r = m->ruleset;
+ while (r != NULL)
+ {
+ if ((r->action == ACTION_OUT_DEST) && (r->dst != NULL) && (r->dst->path != NULL) && (!strcmp(r->dst->path, path))) return r->dst;
+ r = r->next;
+ }
+
+ m = m->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Create a directory path.
+ *
+ * mlist provides owner, group, and access mode, which are required for "non-standard"
+ * directories. Directories for standard paths (/var/log or /Library/Logs) default
+ * to root/admin/0755 if there is no mlist rule for them.
+ */
+static int
+_asl_common_make_dir_path(asl_out_module_t *mlist, uint32_t flags, const char *path)
+{
+ int i;
+ char **path_parts;
+ asl_string_t *processed_path;
+ mode_t mode;
+
+ if (path == NULL) return 0;
+
+ path_parts = explode(path, "/");
+ if (path_parts == NULL) return 0;
+
+ processed_path = asl_string_new(ASL_ENCODE_NONE);
+
+ i = 0;
+ if (path[0] == '/') i = 1;
+
+ for (; path_parts[i] != NULL; i++)
+ {
+ struct stat sb;
+ int status;
+ mode_t mask;
+ asl_out_dst_data_t *dst;
+ char *tmp;
+
+ asl_string_append_char_no_encoding(processed_path, '/');
+ asl_string_append_no_encoding(processed_path, path_parts[i]);
+ tmp = asl_string_bytes(processed_path);
+
+ memset(&sb, 0, sizeof(struct stat));
+ status = lstat(tmp, &sb);
+ if ((status == 0) && S_ISLNK(sb.st_mode))
+ {
+ char real[MAXPATHLEN];
+ if (realpath(tmp, real) == NULL)
+ {
+ asl_string_release(processed_path);
+ free_string_list(path_parts);
+ return -1;
+ }
+
+ memset(&sb, 0, sizeof(struct stat));
+ status = stat(real, &sb);
+ }
+
+ if (status == 0)
+ {
+ if (!S_ISDIR(sb.st_mode))
+ {
+ /* path component is not a directory! */
+ asl_string_release(processed_path);
+ free_string_list(path_parts);
+ return -1;
+ }
+
+ /* exists and is a directory or a link to a directory */
+ continue;
+ }
+ else if (errno != ENOENT)
+ {
+ /* unexpected status from stat() */
+ asl_string_release(processed_path);
+ free_string_list(path_parts);
+ return -1;
+ }
+
+ dst = _asl_out_dest_for_path(mlist, tmp);
+ if ((dst == NULL) && (flags & MODULE_FLAG_NONSTD_DIR))
+ {
+ /* no rule to create a non-standard path component! */
+ asl_string_release(processed_path);
+ free_string_list(path_parts);
+ return -1;
+ }
+
+ mode = 0755;
+ if (dst != NULL)
+ {
+ mode = dst->mode;
+ if (mode == 010000) mode = 0755;
+ }
+
+ mask = umask(0);
+ status = mkdir(tmp, mode);
+ umask(mask);
+
+#if !TARGET_IPHONE_SIMULATOR
+ uid_t u = 0;
+ gid_t g = 80;
+
+ if (dst != NULL)
+ {
+ if (dst->nuid > 0) u = dst->uid[0];
+ if (dst->ngid > 0) g = dst->gid[0];
+ }
+
+ chown(tmp, u, g);
+#endif
+ }
+
+ asl_string_release(processed_path);
+ free_string_list(path_parts);
+
+ return 0;
+}
+
int
-asl_out_mkpath(asl_out_rule_t *r)
+asl_out_mkpath(asl_out_module_t *mlist, asl_out_rule_t *r)
{
char tmp[MAXPATHLEN], *p;
struct stat sb;
status = stat(tmp, &sb);
if (status == 0)
{
- if (!S_ISDIR(sb.st_mode)) return -1;
+ if (S_ISDIR(sb.st_mode)) return 0;
+ return -1;
}
- else if (errno == ENOENT)
+
+ if (errno == ENOENT)
{
- status = mkpath_np(tmp, 0755);
+ uint32_t dirflag = r->dst->flags & MODULE_FLAG_NONSTD_DIR;
+ status = _asl_common_make_dir_path(mlist, dirflag, tmp);
+ return status;
}
- return status;
+ return -1;
}
void
if (dst == NULL) return;
if (buf == NULL) return;
- if (dst->flags & MODULE_FLAG_BASESTAMP)
+ if (dst->flags & (MODULE_FLAG_BASESTAMP | MODULE_FLAG_TYPE_ASL_DIR))
{
char tstamp[32];
+ const char *name = dst->path;
+
+ if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR) name = dst->fname;
if (dst->stamp == 0) dst->stamp = time(NULL);
asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp));
- snprintf(buf, len, "%s.%s", dst->path, tstamp);
+ snprintf(buf, len, "%s.%s", name, tstamp);
}
else
{
}
int
-asl_out_dst_checkpoint(asl_out_dst_data_t *dst, uint32_t force)
-{
- char newpath[MAXPATHLEN];
- time_t now;
-
- now = time(NULL);
-
- /* clock went backwards - force a reset */
- if (now < start_today) start_today = 0;
-
- /* check start_today and reset if required */
- if (now >= (start_today + SECONDS_PER_DAY))
- {
- /* use localtime / mktime since start_today might be zero */
- struct tm t;
-
- start_today = now;
-
- localtime_r(&start_today, &t);
-
- t.tm_sec = 0;
- t.tm_min = 0;
- t.tm_hour = 0;
-
- start_today = mktime(&t);
- }
-
- /* sleep to prevent a sub-second rotation */
- while (now == dst->stamp)
- {
- sleep(1);
- now = time(NULL);
- }
-
- if ((dst->stamp == 0) || (dst->size == 0))
- {
- struct stat sb;
-
- memset(&sb, 0, sizeof(struct stat));
-
- if (stat(dst->path, &sb) < 0)
- {
- if (errno == ENOENT) return 0;
- return -1;
- }
-
- if (dst->stamp == 0) dst->stamp = sb.st_birthtimespec.tv_sec;
- if (dst->stamp == 0) dst->stamp = sb.st_mtimespec.tv_sec;
- dst->size = sb.st_size;
- }
-
- if (force == CHECKPOINT_TEST)
- {
- if ((dst->file_max > 0) && (dst->size >= dst->file_max)) force |= CHECKPOINT_SIZE;
- if (dst->stamp < start_today) force |= CHECKPOINT_TIME;
-
- if (force == CHECKPOINT_TEST) return 0;
- }
-
- if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR)
- {
- if (force & CHECKPOINT_SIZE)
- {
- snprintf(newpath, sizeof(newpath), "%s.%c%lu", dst->fname, STYLE_SEC_PREFIX_CHAR, dst->stamp);
- rename(dst->fname, newpath);
- }
- else
- {
- return 0;
- }
- }
-
- if ((dst->flags & MODULE_FLAG_BASESTAMP) == 0)
- {
- char tstamp[32];
-
- asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp));
- snprintf(newpath, sizeof(newpath), "%s.%s", dst->path, tstamp);
- rename(dst->path, newpath);
- }
-
- dst->stamp = 0;
- dst->size = 0;
-
- return 1;
-}
-
-int
-asl_check_option(aslmsg msg, const char *opt)
+asl_check_option(asl_msg_t *msg, const char *opt)
{
const char *p;
uint32_t len;
len = strlen(opt);
if (len == 0) return 0;
- p = asl_get(msg, ASL_KEY_OPTION);
+ p = asl_msg_get_val_for_key(msg, ASL_KEY_OPTION);
if (p == NULL) return 0;
while (*p != '\0')
* Don't bother setting group access if this is
* file's group and the file is group-readable.
*/
- if ((dst->gid[i] == fgid) && (dst->mode & 0040)) continue;
+ if ((dst->gid[i] == fgid) && (dst->mode & 00040)) continue;
status = mbr_gid_to_uuid(dst->gid[i], uuid);
if (status != 0)
* Don't bother setting user access if this is
* file's owner and the file is owner-readable.
*/
- if ((dst->uid[i] == fuid) && (dst->mode & 0400)) continue;
+ if ((dst->uid[i] == fuid) && (dst->mode & 00400)) continue;
status = mbr_uid_to_uuid(dst->uid[i], uuid);
if (status != 0)
/* create a file with acls */
int
-asl_out_dst_file_create_open(asl_out_dst_data_t *dst)
+asl_out_dst_file_create_open(asl_out_dst_data_t *dst, char **pathp)
{
int fd, status;
struct stat sb;
if (dst->path == NULL) return -1;
asl_make_dst_filename(dst, outpath, sizeof(outpath));
+ if (dst->fname != NULL) free(dst->fname);
+
+ dst->fname = strdup(outpath);
+ if (dst->fname == NULL) return -1;
+
+ if (pathp != NULL) *pathp = strdup(outpath);
memset(&sb, 0, sizeof(struct stat));
status = stat(outpath, &sb);
return -1;
}
- fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 0666));
+ fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 00666));
if (fd < 0) return -1;
dst->stamp = time(NULL);
char *p, *opts, *path;
char **path_parts;
int has_dotdot, recursion_limit;
+ uint32_t i, flags = 0;
if (m == NULL) return NULL;
if (s == NULL) return NULL;
}
free_string_list(path_parts);
+ path_parts = NULL;
- if ((did_sub == 0) || (has_dotdot == 1))
- {
- path_parts = NULL;
- }
- else
+ if ((did_sub == 1) && (has_dotdot == 0))
{
/* substitution might have added a ".." so check the new path */
free(path);
- path = asl_string_free_return_bytes(processed_path);
+ path = asl_string_release_return_bytes(processed_path);
processed_path = asl_string_new(ASL_ENCODE_NONE);
path_parts = explode(path, "/");
recursion_limit--;
}
free(path);
+ free_string_list(path_parts);
+ path_parts = NULL;
if ((has_dotdot != 0) || (recursion_limit == 0))
{
- asl_string_free(processed_path);
+ asl_string_release(processed_path);
return NULL;
}
- path = asl_string_free_return_bytes(processed_path);
+ path = asl_string_release_return_bytes(processed_path);
/* check if there's already a dst for this path */
for (rule = m->ruleset; rule != NULL; rule = rule->next)
}
}
+ flags |= MODULE_FLAG_NONSTD_DIR;
+
if (path[0] != '/')
{
char *t = path;
const char *log_root = "/var/log";
#if TARGET_IPHONE_SIMULATOR
- log_root = getenv("IPHONE_SIMULATOR_LOG_ROOT");
- assert(log_root);
+ log_root = getenv("SIMULATOR_LOG_ROOT");
+ if (log_root == NULL) log_root = "/tmp/log";
#endif
- if (!strcmp(m->name, ASL_MODULE_NAME)) asprintf(&path, "%s/%s", log_root, t);
- else asprintf(&path, "%s/module/%s/%s", log_root, m->name, t);
+ if (!strcmp(m->name, ASL_MODULE_NAME))
+ {
+ asprintf(&path, "%s/%s", log_root, t);
+ }
+ else
+ {
+ asprintf(&path, "%s/module/%s/%s", log_root, m->name, t);
+ }
free(t);
+ flags &= ~MODULE_FLAG_NONSTD_DIR;
+ }
+ else
+ {
+ /*
+ * Standard log directories get marked so that syslogd
+ * will create them without explicit rules.
+ */
+ if (!strncmp(path, PATH_VAR_LOG, PATH_VAR_LOG_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR;
+ else if (!strncmp(path, PATH_LIBRARY_LOGS, PATH_LIBRARY_LOGS_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR;
}
out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
dst->refcount = 1;
dst->path = path;
dst->mode = def_mode;
- dst->ttl = DEFAULT_TTL;
- dst->flags = MODULE_FLAG_COALESCE;
+ dst->ttl[LEVEL_ALL] = DEFAULT_TTL;
+ dst->flags = flags | MODULE_FLAG_COALESCE;
while (NULL != (p = next_word_from_string(&opts)))
{
if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0);
- else if (KEYMATCH(p, "ttl=")) dst->ttl = strtol(p+4, NULL, 0);
#if !TARGET_IPHONE_SIMULATOR
else if (KEYMATCH(p, "uid=")) _dst_add_uid(dst, p+4);
else if (KEYMATCH(p, "gid=")) _dst_add_gid(dst, p+4);
}
else if (KEYMATCH(p, "compress")) dst->flags |= MODULE_FLAG_COMPRESS;
else if (KEYMATCH(p, "extern")) dst->flags |= MODULE_FLAG_EXTERNAL;
+ else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE;
+ else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE;
else if (KEYMATCH(p, "file_max=")) dst->file_max = asl_str_to_size(p+9);
else if (KEYMATCH(p, "all_max=")) dst->all_max = asl_str_to_size(p+8);
{
dst->flags |= MODULE_FLAG_BASESTAMP;
}
+ else if (KEYMATCH(p, "ttl"))
+ {
+ char *q = p + 3;
+ if (*q == '=')
+ {
+ dst->ttl[LEVEL_ALL] = strtol(p+4, NULL, 0);
+ }
+ else if ((*q >= '0') && (*q <= '7') && (*(q+1) == '='))
+ {
+ uint32_t x = *q - '0';
+ dst->ttl[x] = strtol(p+5, NULL, 0);
+ }
+ }
free(p);
p = NULL;
#if TARGET_OS_EMBEDDED
/* check for crashreporter files */
- if (KEYMATCH(dst->path, _PATH_CRASHREPORTER))
+ if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE)))
{
dst->flags |= MODULE_FLAG_ROTATE;
dst->flags |= MODULE_FLAG_CRASHLOG;
}
#endif
+ /* ttl[LEVEL_ALL] must be max of all level-specific ttls */
+ for (i = 0; i <= 7; i++) if (dst->ttl[i] > dst->ttl[LEVEL_ALL]) dst->ttl[LEVEL_ALL] = dst->ttl[i];
+
/* default text file format is "std" */
if (dst->fmt == NULL) dst->fmt = strdup("std");
if (strcmp(dst->fmt, "std") && strcmp(dst->fmt, "bsd")) dst->flags &= ~MODULE_FLAG_COALESCE;
/* note if format is one of std, bsd, or msg */
- if ((!strcmp(dst->fmt, "std")) || (!strcmp(dst->fmt, "bsd")) || (!strcmp(dst->fmt, "msg"))) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
+ if (!strcmp(dst->fmt, "std") || !strcmp(dst->fmt, "bsd") || !strcmp(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
/* MODULE_FLAG_STYLE_SEQ can not be used with MODULE_FLAG_BASESTAMP */
if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_STYLE_SEQ))
/* set time format for raw output */
if (!strcmp(dst->fmt, "raw")) dst->tfmt = "sec";
+ /* check for ASL_PLACE_DATABASE_DEFAULT */
+ if (!strcmp(dst->path, ASL_PLACE_DATABASE_DEFAULT))
+ {
+ dst->flags = MODULE_FLAG_TYPE_ASL_DIR;
+ }
+
out->action = ACTION_OUT_DEST;
out->dst = dst;
else if (!strcasecmp(act, "save")) out->action = ACTION_ASL_STORE;
else if (!strcasecmp(act, "store")) out->action = ACTION_ASL_STORE;
else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS;
+ else if (!strcasecmp(act, "set")) out->action = ACTION_SET_KEY;
+ else if (!strcasecmp(act, "unset")) out->action = ACTION_UNSET_KEY;
else if (!strcmp(m->name, ASL_MODULE_NAME))
{
/* actions only allowed in com.apple.asl */
}
/* store /some/path means save to an asl file */
- if ((out->action == ACTION_ASL_STORE) && (out->options != NULL)) out->action = ACTION_ASL_FILE;
+ if (out->action == ACTION_ASL_STORE)
+ {
+ if (out->options == NULL) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, ASL_PLACE_DATABASE_DEFAULT, 0755));
+ else if (!strncmp(out->options, ASL_PLACE_DATABASE_DEFAULT, strlen(ASL_PLACE_DATABASE_DEFAULT))) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, 0755));
+ else if (out->options != NULL) out->action = ACTION_ASL_FILE;
+ }
if ((out->action == ACTION_FILE) || (out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR))
{
return out;
}
+ /*
+ * dst might have been set up by a previous ACTION_OUT_DEST ('>') rule with no mode.
+ * If so, mode would be 010000. Set it now, since we know whether it is a file or dir.
+ */
+ if (out->dst->mode == 010000) out->dst->mode = def_mode;
+
if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (!strcasecmp(out->dst->fmt, "asl")))
{
out->action = ACTION_ASL_FILE;
if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL))
{
- /* remove meaningless flags */
- out->dst->flags &= ~MODULE_FLAG_COALESCE;
- out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG;
out->dst->flags |= MODULE_FLAG_TYPE_ASL;
}
if (out->action == ACTION_ASL_DIR)
{
- /* remove meaningless flags */
- out->dst->flags &= ~MODULE_FLAG_ROTATE;
+ /* coalesce is meaningless for ASL directories */
out->dst->flags &= ~MODULE_FLAG_COALESCE;
- out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG;
+
+ /* no compression at this point */
+ out->dst->flags &= ~MODULE_FLAG_COMPRESS;
+
out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
+
+ /* set style bits for basestamp asl_dirs */
+ if (((out->dst->flags & MODULE_FLAG_STYLE_BITS) == 0) && (out->dst->flags & MODULE_FLAG_BASESTAMP)) out->dst->flags |= MODULE_FLAG_STYLE_LCL_B;
}
/* only ACTION_FILE and ACTION_ASL_FILE may rotate */
}
else if (*s == '>')
{
- _asl_out_module_parse_dst(m, s + 1, 0644);
+ _asl_out_module_parse_dst(m, s + 1, 010000);
}
return NULL;
{
asl_out_rule_t *r, *n;
asl_out_dst_data_t *o;
- uint32_t i;
+ uint32_t i, ttlnset;
n = NULL;
for (r = m->ruleset; r != NULL; r = n)
fprintf(f, "%cenabled", c);
c = ' ';
}
- if (o->flags & MODULE_FLAG_LOCAL)
+ if (o->flags & MODULE_FLAG_SOFT_WRITE)
{
- fprintf(f, "%clocal", c);
+ fprintf(f, "%csoft", c);
c = ' ';
}
if (o->flags & MODULE_FLAG_ROTATE)
fprintf(f, "%ccompress", c);
c = ' ';
}
- if (o->flags & MODULE_FLAG_EXTERNAL)
- {
- fprintf(f, "%cexternal", c);
- c = ' ';
- }
if (o->flags & MODULE_FLAG_STYLE_SEC)
{
fprintf(f, "%cseconds", c);
fprintf(f, "%cbasestamp", c);
c = ' ';
}
- if (o->flags & MODULE_FLAG_CRASHLOG)
+ if (o->flags & MODULE_FLAG_NONSTD_DIR)
{
- fprintf(f, "%ccrashlog", c);
+ fprintf(f, "%cnon-std_dir", c);
c = ' ';
}
- if (o->flags & MODULE_FLAG_SOFT_WRITE)
+ if (o->flags & MODULE_FLAG_EXTERNAL)
{
- fprintf(f, "%csoft", c);
+ fprintf(f, "%cexternal", c);
+ c = ' ';
+ }
+ if (o->flags & MODULE_FLAG_CRASHLOG)
+ {
+ fprintf(f, "%ccrashlog", c);
c = ' ';
}
if (o->flags & MODULE_FLAG_TYPE_ASL)
fprintf(f, "%casl_directory", c);
c = ' ';
}
- if (o->flags & MODULE_FLAG_STD_BSD_MSG)
- {
- fprintf(f, "%cstd/bsd/msg", c);
- c = ' ';
- }
fprintf(f, ")");
}
fprintf(f, "\n");
- fprintf(f, " ttl: %u\n", o->ttl);
+ fprintf(f, " ttl: %u", o->ttl[LEVEL_ALL]);
+ ttlnset = 0;
+ for (i = 0; (i <= 7) & (ttlnset == 0); i++) if (o->ttl[i] != 0) ttlnset = 1;
+ if (ttlnset != 0) for (i = 0; i <= 7; i++) printf(" [%d %d]", i, (o->ttl[i] == 0) ? o->ttl[LEVEL_ALL] : o->ttl[i]);
+ fprintf(f, "\n");
+
fprintf(f, " mode: 0%o\n", o->mode);
fprintf(f, " file_max: %lu\n", o->file_max);
fprintf(f, " all_max: %lu\n", o->all_max);
* MODULE_FLAG_STYLE_LCL_B requires a date/time component as the file's timestamp.
* E.g. foo.20120406T153000-07
*/
+int
+_parse_stamp_style(char *stamp, uint32_t flags, uint32_t *sp, time_t *tp)
+{
+ int i, n;
+ bool digits;
+ struct tm t;
+ char zone;
+ uint32_t h, m, s;
+ long utc_offset = 0;
+ time_t ftime = 0;
+
+ /* check for NULL (no stamp) */
+ if (stamp == NULL) return STAMP_STYLE_NULL;
+
+ /* check for MODULE_FLAG_STYLE_SEC (foo.T12345678) */
+ if (stamp[0] == 'T')
+ {
+ n = atoi(stamp + 1);
+ if ((n == 0) && strcmp(stamp + 1, "0")) return STAMP_STYLE_INVALID;
+ if (tp != NULL) *tp = (time_t)n;
+
+ return STAMP_STYLE_SEC;
+ }
+
+ /* check for MODULE_FLAG_STYLE_SEQ (foo.0 or foo.2.gz) */
+ digits = true;
+ for (i = 0; digits && (stamp[i] != '\0'); i++) digits = (stamp[i] >= '0') && (stamp[i] <= '9');
+
+ if (!digits && (!strcmp(stamp + i, ".gz"))) digits = true;
+
+ if (digits)
+ {
+ n = atoi(stamp);
+ if (sp != NULL) *sp = (uint32_t)n;
+ return STAMP_STYLE_SEQ;
+ }
+
+ /* check for MODULE_FLAG_STYLE_UTC, UTC_B, LCL, or LCL_B */
+ memset(&t, 0, sizeof(t));
+ h = m = s = 0;
+
+ n = 0;
+ if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL))
+ {
+ n = sscanf(stamp, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
+ }
+ else if ((flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL_B))
+ {
+ n = sscanf(stamp, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
+ }
+ else
+ {
+ return STAMP_STYLE_INVALID;
+ }
+
+ if (n < 6) return STAMP_STYLE_INVALID;
+
+ if (n == 6)
+ {
+ zone = 'J';
+ }
+ else if ((zone == '-') || (zone == '+'))
+ {
+ if (n >= 8) utc_offset += (3600 * h);
+ if (n >= 9) utc_offset += (60 * m);
+ if (n == 10) utc_offset += s;
+ if (zone == '-') utc_offset *= -1;
+ }
+ else if ((zone >= 'A') && (zone <= 'Z'))
+ {
+ if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1);
+ else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A');
+ else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1);
+ }
+ else if ((zone >= 'a') && (zone <= 'z'))
+ {
+ if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1);
+ else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a');
+ else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1);
+ }
+ else
+ {
+ return STAMP_STYLE_INVALID;
+ }
+
+ t.tm_year -= 1900;
+ t.tm_mon -= 1;
+ t.tm_sec += utc_offset;
+ t.tm_isdst = -1;
+
+ if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t);
+ else ftime = timegm(&t);
+
+ if (tp != NULL) *tp = ftime;
+
+ return STAMP_STYLE_UTC_OR_LCL;
+}
+
asl_out_file_list_t *
asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags)
{
uint32_t seq;
time_t ftime;
struct stat sb;
- int n;
+ int pstyle, fstyle;
asl_out_file_list_t *out, *x, *y;
if (dir == NULL) return NULL;
check = _check_file_name(ent->d_name, base, src, &stamp);
if (!check) continue;
- /* exclude base from dst list */
-
seq = IndexNull;
ftime = 0;
- if (stamp == NULL)
- {
- }
- else if (flags & MODULE_FLAG_STYLE_SEQ)
- {
- seq = atoi(stamp);
- if ((seq == 0) && strcmp(stamp, "0"))
- {
- free(stamp);
- continue;
- }
- }
- else if (flags & MODULE_FLAG_STYLE_SEC)
- {
- ftime = atoi(stamp + 1);
- }
- else if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL) || (flags & MODULE_FLAG_STYLE_LCL_B))
- {
- struct tm t;
- char zone;
- uint32_t h, m, s;
- long utc_offset = 0;
-
- memset(&t, 0, sizeof(t));
- h = m = s = 0;
+ pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime);
+ free(stamp);
- n = 0;
- if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL))
- {
- n = sscanf(stamp, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
- }
- else
- {
- n = sscanf(stamp, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
- }
+ if (pstyle == STAMP_STYLE_INVALID) continue;
- if (n < 6)
- {
- continue;
- }
- else if (n == 6)
- {
- zone = 'J';
- }
- else if ((zone == '-') || (zone == '+'))
- {
- if (n >= 8) utc_offset += (3600 * h);
- if (n >= 9) utc_offset += (60 * m);
- if (n == 10) utc_offset += s;
- if (zone == '-') utc_offset *= -1;
- }
- else if ((zone >= 'A') && (zone <= 'Z'))
- {
- if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1);
- else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A');
- else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1);
- }
- else if ((zone >= 'a') && (zone <= 'z'))
- {
- if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1);
- else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a');
- else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1);
- }
- else
- {
- free(stamp);
- continue;
- }
+ fstyle = STAMP_STYLE_NULL;
+ if (flags & MODULE_FLAG_STYLE_SEC) fstyle = STAMP_STYLE_SEC;
+ else if (flags & MODULE_FLAG_STYLE_SEQ) fstyle = STAMP_STYLE_SEQ;
+ else if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL)) fstyle = STAMP_STYLE_UTC_OR_LCL;
+ else if ((flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL_B)) fstyle = STAMP_STYLE_UTC_OR_LCL;
- t.tm_year -= 1900;
- t.tm_mon -= 1;
- t.tm_sec += utc_offset;
- t.tm_isdst = -1;
+ /*
+ * accept the file if:
+ * style is STAMP_STYLE_NULL (no timestamp)
+ * src is true and style is STAMP_STYLE_SEC
+ * actual style matches the style implied by the input flags
+ */
- if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t);
- else ftime = timegm(&t);
- }
+ check = false;
+ if (pstyle == STAMP_STYLE_NULL) check = true;
+ if ((pstyle == STAMP_STYLE_SEC) && src) check = true;
+ if (pstyle == fstyle) check = true;
- free(stamp);
+ if (!check) continue;
x = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
if (x == NULL)
if (stat(path, &sb) == 0)
{
x->size = sb.st_size;
- if (flags & MODULE_FLAG_STYLE_SEQ)
+ if (pstyle == STAMP_STYLE_SEQ)
{
x->ftime = sb.st_birthtimespec.tv_sec;
if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
}
}
- if (flags & MODULE_FLAG_STYLE_SEQ)
+ if (pstyle == STAMP_STYLE_SEQ)
{
if (out == NULL)
{
/*
* MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
- * We simply check for its existence.
+ * We check for its existence, and that it is non-zero in size.
*/
if (dst->flags & MODULE_FLAG_EXTERNAL)
{
if (stat(dst->path, &sb) == 0)
{
- if (S_ISREG(sb.st_mode))
+ if (S_ISREG(sb.st_mode) && (sb.st_size != 0))
{
out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
if (out != NULL)
return out;
}
+
+static int
+asl_secure_open_dir(const char *path)
+{
+ int fd, i;
+ char **path_parts;
+
+ if (path == NULL) return -1;
+ if (path[0] != '/') return -1;
+
+ path_parts = explode(path + 1, "/");
+ if (path_parts == NULL) return 0;
+
+ fd = open("/", O_RDONLY | O_NOFOLLOW, 0);
+ if (fd < 0)
+ {
+ free_string_list(path_parts);
+ return -1;
+ }
+
+ for (i = 0; path_parts[i] != NULL; i++)
+ {
+ int fd_next, status;
+ struct stat sb;
+
+ fd_next = openat(fd, path_parts[i], O_RDONLY | O_NOFOLLOW, 0);
+ close(fd);
+ fd = fd_next;
+ if (fd < 0)
+ {
+ free_string_list(path_parts);
+ return -1;
+ }
+
+ memset(&sb, 0, sizeof(sb));
+
+ status = fstat(fd, &sb);
+ if (status < 0)
+ {
+ free_string_list(path_parts);
+ return -1;
+ }
+
+ if (!S_ISDIR(sb.st_mode))
+ {
+ free_string_list(path_parts);
+ return -1;
+ }
+ }
+
+ free_string_list(path_parts);
+ return fd;
+}
+
+int
+asl_secure_chown_chmod_dir(const char *path, uid_t uid, gid_t gid, mode_t mode)
+{
+ int fd, status;
+
+ fd = asl_secure_open_dir(path);
+ if (fd < 0) return fd;
+
+ status = fchown(fd, uid, gid);
+ if (status < 0)
+ {
+ close(fd);
+ return -1;
+ }
+
+ if (mode >= 0) status = fchmod(fd, mode);
+ close(fd);
+
+ if (status < 0) return -1;
+ return 0;
+}