/* * Copyright (c) 2007-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #define ASL_STRING_QUANTUM 256 static const char *cvis_7_13 = "abtnvfr"; /* * Message ID generation */ static uint64_t _asl_core_msg_next_id = 1; static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER; #define mix(a, b, c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<< 8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>> 5); \ a -= b; a -= c; a ^= (c>> 3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } /* * Hash is used to improve string search. */ uint32_t asl_core_string_hash(const char *s, uint32_t inlen) { uint32_t a, b, c, l, len; if (s == NULL) return 0; l = inlen; if (l == 0) { if (s[0] == '\0') return 0; l = strlen(s); } len = l; a = b = 0x9e3779b9; c = 0; while (len >= 12) { a += (s[0] + ((uint32_t)s[1]<<8) + ((uint32_t)s[ 2]<<16) + ((uint32_t)s[ 3]<<24)); b += (s[4] + ((uint32_t)s[5]<<8) + ((uint32_t)s[ 6]<<16) + ((uint32_t)s[ 7]<<24)); c += (s[8] + ((uint32_t)s[9]<<8) + ((uint32_t)s[10]<<16) + ((uint32_t)s[11]<<24)); mix(a, b, c); s += 12; len -= 12; } c += l; switch(len) { case 11: c += ((uint32_t)s[10]<<24); case 10: c += ((uint32_t)s[9]<<16); case 9 : c += ((uint32_t)s[8]<<8); case 8 : b += ((uint32_t)s[7]<<24); case 7 : b += ((uint32_t)s[6]<<16); case 6 : b += ((uint32_t)s[5]<<8); case 5 : b += s[4]; case 4 : a += ((uint32_t)s[3]<<24); case 3 : a += ((uint32_t)s[2]<<16); case 2 : a += ((uint32_t)s[1]<<8); case 1 : a += s[0]; } mix(a, b, c); if (c == 0) c = 1; return c; } const char * asl_core_error(uint32_t code) { switch (code) { case ASL_STATUS_OK: return "Operation Succeeded"; case ASL_STATUS_INVALID_ARG: return "Invalid Argument"; case ASL_STATUS_INVALID_STORE: return "Invalid Data Store"; case ASL_STATUS_INVALID_STRING: return "Invalid String"; case ASL_STATUS_INVALID_ID: return "Invalid ID Number"; case ASL_STATUS_INVALID_MESSAGE: return "Invalid Message"; case ASL_STATUS_NOT_FOUND: return "Not Found"; case ASL_STATUS_READ_FAILED: return "Read Operation Failed"; case ASL_STATUS_WRITE_FAILED: return "Write Operation Failed"; case ASL_STATUS_NO_MEMORY: return "System Memory Allocation Failed"; case ASL_STATUS_ACCESS_DENIED: return "Access Denied"; case ASL_STATUS_READ_ONLY: return "Read Only Access"; case ASL_STATUS_WRITE_ONLY: return "Write Only Access"; case ASL_STATUS_MATCH_FAILED: return "Match Failed"; case ASL_STATUS_NO_RECORDS: return "No More Records"; } return "Operation Failed"; } static uint32_t asl_core_check_user_access(int32_t msgu, int32_t readu) { /* -1 means anyone may read */ if (msgu == -1) return ASL_STATUS_OK; /* Check for exact match */ if (msgu == readu) return ASL_STATUS_OK; return ASL_STATUS_ACCESS_DENIED; } static uint32_t asl_core_check_group_access(int32_t msgg, int32_t readu, int32_t readg) { int check; uuid_t uu, gu; /* -1 means anyone may read */ if (msgg == -1) return ASL_STATUS_OK; /* Check for exact match */ if (msgg == readg) return ASL_STATUS_OK; /* Check if user (u) is in read group (msgg) */ mbr_uid_to_uuid(readu, uu); mbr_gid_to_uuid(msgg, gu); check = 0; mbr_check_membership(uu, gu, &check); if (check != 0) return ASL_STATUS_OK; return ASL_STATUS_ACCESS_DENIED; } uint32_t asl_core_check_access(int32_t msgu, int32_t msgg, int32_t readu, int32_t readg, uint16_t flags) { uint16_t uset, gset; /* root (uid 0) may always read */ if (readu == 0) return ASL_STATUS_OK; uset = flags & ASL_MSG_FLAG_READ_UID_SET; gset = flags & ASL_MSG_FLAG_READ_GID_SET; /* if no access controls are set, anyone may read */ if ((uset | gset) == 0) return ASL_STATUS_OK; /* if only uid is set, then access is only by uid match */ if ((uset != 0) && (gset == 0)) return asl_core_check_user_access(msgu, readu); /* if only gid is set, then access is only by gid match */ if ((uset == 0) && (gset != 0)) return asl_core_check_group_access(msgg, readu, readg); /* both uid and gid are set - check user, then group */ if ((asl_core_check_user_access(msgu, readu)) == ASL_STATUS_OK) return ASL_STATUS_OK; return asl_core_check_group_access(msgg, readu, readg); } uint64_t asl_core_htonq(uint64_t n) { #ifdef __BIG_ENDIAN__ return n; #else u_int32_t t; union { u_int64_t q; u_int32_t l[2]; } x; x.q = n; t = x.l[0]; x.l[0] = htonl(x.l[1]); x.l[1] = htonl(t); return x.q; #endif } uint64_t asl_core_ntohq(uint64_t n) { #ifdef __BIG_ENDIAN__ return n; #else u_int32_t t; union { u_int64_t q; u_int32_t l[2]; } x; x.q = n; t = x.l[0]; x.l[0] = ntohl(x.l[1]); x.l[1] = ntohl(t); return x.q; #endif } uint64_t asl_core_new_msg_id(uint64_t start) { uint64_t out; pthread_mutex_lock(&core_lock); if (start != 0) _asl_core_msg_next_id = start; out = _asl_core_msg_next_id; _asl_core_msg_next_id++; pthread_mutex_unlock(&core_lock); return out; } /* * asl_core_encode_buffer * encode arbitrary data as a C string without embedded zero (nul) characters * * The routine computes a histogram of the input buffer and finds * the two least frequently used non-nul chars (L[0] and L[1]). * * L[0] is used to stand in for nul. * L[1] is used as the escape character. * Occurrences of nul in the data are encoded as L[0] * Occurrences of L[0] in the data are encoded as the sequence L[1] 1. * Occurrences of L[1] in the data are encoded as the sequence L[1] 2. * * The output string is preceded by L[0] L[1], and is nul terminated. * The output length is 2 + n + N(L[0]) + N(L[1]) + 1 * where N(x) is the number of occurrences of x in the input string. * The worst case occurs when all characters are equally frequent, * In that case the output size will less that 1% larger than the input. */ char * asl_core_encode_buffer(const char *in, uint32_t len) { char *str; uint32_t i, j, k, outlen, breakit, min, hist[256]; uint32_t lfu[2], save[2]; uint8_t v; if (in == NULL) return NULL; if (len == 0) return NULL; memset(hist, 0, sizeof(hist)); save[0] = 0; save[1] = 0; for (i = 0; i < len; i++) { v = in[i]; hist[v]++; } for (j = 0; j < 2; j++) { lfu[j] = 1; min = hist[1]; for (i = 2; i < 256; i++) { if (hist[i] < min) { lfu[j] = i; min = hist[i]; /* * Stop if there are no occurances or character i in the input. * The minimum will never be less than zero. */ if (min == 0) break; /* * When looking for the second least frequently used character, * stop scanning if we hit the same minimum as we saw in the first * pass. There will be no smaller values. */ if ((j == 1) && (min == save[0])) break; } } save[j] = hist[lfu[j]]; hist[lfu[j]] = (uint32_t)-1; } outlen = 2 + len + save[0] + save[1] + 1; str = malloc(outlen); if (str == NULL) return NULL; str[outlen - 1] = '\0'; str[0] = lfu[0]; str[1] = lfu[1]; j = 2; for (i = 0; i < len; i++) { v = in[i]; if (v == 0) { str[j++] = lfu[0]; continue; } breakit = 0; for (k = 0; (k < 2) && (breakit == 0); k++) { if (v == lfu[k]) { str[j++] = lfu[1]; str[j++] = k + 1; breakit = 1; } } if (breakit == 1) continue; str[j++] = v; } return str; } /* * asl_core_decode_buffer * decode a string produced by asl_encode_buffer to recreate the original data */ int32_t asl_core_decode_buffer(const char *in, char **buf, uint32_t *len) { uint8_t v; uint32_t i, j, n, outlen; uint8_t lfu[2]; char *out; if (buf == NULL) return -1; if (len == NULL) return -1; lfu[0] = in[0]; lfu[1] = in[1]; outlen = 0; /* strip trailing nul */ n = strlen(in); /* determine output length and check for invalid input */ for (i = 2; i < n; i++) { v = in[i]; if (v == lfu[1]) { i++; if (i == n) return -1; v = in[i]; if ((v < 1) || (v > 2)) return -1; outlen++; } else outlen++; } if (outlen == 0) return -1; out = malloc(outlen); if (out == NULL) return -1; j = 0; for (i = 2; i < n; i++) { v = in[i]; if (v == lfu[0]) { out[j++] = 0; } else if (v == lfu[1]) { i++; v = in[i]; out[j++] = lfu[v - 1]; } else out[j++] = v; } *len = outlen; *buf = out; return 0; } /* asl_string_t support */ asl_string_t * asl_string_new(uint32_t encoding) { asl_string_t *str = (asl_string_t *)calloc(1, sizeof(asl_string_t)); if (str == NULL) return NULL; str->encoding = encoding; str->delta = ASL_STRING_QUANTUM; if (encoding & ASL_STRING_VM) str->delta = PAGE_SIZE; str->bufsize = 0; str->cursor = 0; if (encoding & ASL_STRING_LEN) asl_string_append_no_encoding(str, " 0 "); return str; } void asl_string_free(asl_string_t *str) { if (str == NULL) return; if (str->encoding & ASL_STRING_VM) { vm_deallocate(mach_task_self(), (vm_address_t)str->buf, str->bufsize); } else { free(str->buf); } free(str); } char * asl_string_free_return_bytes(asl_string_t *str) { char *out; if (str == NULL) return NULL; out = str->buf; free(str); return out; } char * asl_string_bytes(asl_string_t *str) { if (str == NULL) return NULL; return str->buf; } /* length includes trailing nul */ size_t asl_string_length(asl_string_t *str) { if (str == NULL) return 0; if (str->cursor == 0) return 0; return str->cursor + 1; } size_t asl_string_allocated_size(asl_string_t *str) { if (str == NULL) return 0; return str->bufsize; } static int _asl_string_grow(asl_string_t *str, size_t len) { size_t newlen = 0; if (str == NULL) return -1; if (len == 0) return 0; if (str->bufsize == 0) { newlen = ((len + str->delta - 1) / str->delta) * str->delta; } else { /* used size is (str->cursor + 1) including tailiing nul */ if (len <= (str->bufsize - (str->cursor + 1))) return 0; /* really this is ((str->cursor + 1) + len + (str->delta - 1)) */ newlen = ((str->cursor + len + str->delta) / str->delta) * str->delta; } if (str->encoding & ASL_STRING_VM) { kern_return_t kstatus; vm_address_t new = 0; kstatus = vm_allocate(mach_task_self(), &new, newlen, TRUE); if (kstatus != KERN_SUCCESS) { new = 0; newlen = 0; return -1; } if (str->buf != NULL) { memcpy((void *)new, str->buf, str->bufsize); vm_deallocate(mach_task_self(), (vm_address_t)str->buf, str->bufsize); } str->buf = (char *)new; str->bufsize = newlen; } else { str->buf = reallocf(str->buf, newlen); if (str->buf == NULL) { str->cursor = 0; str->bufsize = 0; return -1; } str->bufsize = newlen; } return 0; } asl_string_t * asl_string_append_char_no_encoding(asl_string_t *str, const char c) { size_t len; if (str == NULL) return NULL; len = 1; if (str->bufsize == 0) len++; if (_asl_string_grow(str, len) < 0) return str; str->buf[str->cursor] = c; str->cursor++; str->buf[str->cursor] = '\0'; if (str->encoding & ASL_STRING_LEN) { char tmp[11]; snprintf(tmp, sizeof(tmp), "%10lu", str->cursor - 10); memcpy(str->buf, tmp, 10); } return str; } asl_string_t * asl_string_append_no_encoding(asl_string_t *str, const char *app) { size_t len, applen; if (str == NULL) return NULL; if (app == NULL) return str; applen = strlen(app); len = applen; if (str->bufsize == 0) len++; if (_asl_string_grow(str, len) < 0) return str; memcpy(str->buf + str->cursor, app, applen); str->cursor += applen; str->buf[str->cursor] = '\0'; if (str->encoding & ASL_STRING_LEN) { char tmp[11]; snprintf(tmp, sizeof(tmp), "%10lu", str->cursor - 10); memcpy(str->buf, tmp, 10); } return str; } static asl_string_t * asl_string_append_internal(asl_string_t *str, const char *app, int encode_space) { uint8_t x; const char *p; if (str == NULL) return NULL; if (app == NULL) return str; switch (str->encoding & ASL_ENCODE_MASK) { case ASL_ENCODE_NONE: { return asl_string_append_no_encoding(str, app); } case ASL_ENCODE_SAFE: { /* minor encoding to reduce the likelyhood of spoof attacks */ const char *p; for (p = app; *p != '\0'; p++) { if ((*p == 10) || (*p == 13)) { asl_string_append_no_encoding(str, "\n\t"); } else if (*p == 8) { asl_string_append_no_encoding(str, "^H"); } else { asl_string_append_char_no_encoding(str, *p); } } return str; } case ASL_ENCODE_ASL: { for (p = app; *p != '\0'; p++) { int meta = 0; x = *p; /* Meta chars get \M prefix */ if (x >= 128) { /* except meta-space, which is \240 */ if (x == 160) { asl_string_append_no_encoding(str, "\\240"); continue; } asl_string_append_no_encoding(str, "\\M"); x &= 0x7f; meta = 1; } /* space is either ' ' or \s */ if (x == 32) { if (encode_space == 0) { asl_string_append_char_no_encoding(str, ' '); continue; } asl_string_append_no_encoding(str, "\\s"); continue; } /* \ is escaped */ if ((meta == 0) && (x == 92)) { asl_string_append_no_encoding(str, "\\\\"); continue; } /* [ and ] are escaped in ASL encoding */ if ((str->encoding & ASL_ENCODE_ASL) && (meta == 0) && ((*p == 91) || (*p == 93))) { if (*p == '[') asl_string_append_no_encoding(str, "\\["); else asl_string_append_no_encoding(str, "\\]"); continue; } /* DEL is \^? */ if (x == 127) { if (meta == 0) { asl_string_append_char_no_encoding(str, '\\'); } asl_string_append_no_encoding(str, "^?"); continue; } /* 33-126 are printable (add a '-' prefix for meta) */ if ((x >= 33) && (x <= 126)) { if (meta == 1) { asl_string_append_char_no_encoding(str, '-'); } asl_string_append_char_no_encoding(str, x); continue; } /* non-meta BEL, BS, HT, NL, VT, NP, CR (7-13) are \a, \b, \t, \n, \v, \f, and \r */ if ((meta == 0) && (x >= 7) && (x <= 13)) { asl_string_append_char_no_encoding(str, '\\'); asl_string_append_char_no_encoding(str, cvis_7_13[x - 7]); continue; } /* 0 - 31 are ^@ - ^_ (non-meta get a leading \) */ if (x <= 31) { if (meta == 0) { asl_string_append_char_no_encoding(str, '\\'); } asl_string_append_char_no_encoding(str, '^'); asl_string_append_char_no_encoding(str, 64 + x); continue; } asl_string_append_char_no_encoding(str, x); } return str; } case ASL_ENCODE_XML: { for (p = app; *p != '\0'; p++) { x = *p; if (x == '&') { asl_string_append_no_encoding(str, "&"); } else if (x == '<') { asl_string_append_no_encoding(str, "<"); } else if (x == '>') { asl_string_append_no_encoding(str, ">"); } else if (x == '"') { asl_string_append_no_encoding(str, """); } else if (x == '\'') { asl_string_append_no_encoding(str, "'"); } else if (iscntrl(x)) { char tmp[8]; snprintf(tmp, sizeof(tmp), "&#x%02hhx;", x); asl_string_append_no_encoding(str, tmp); } else { asl_string_append_char_no_encoding(str, x); } } } default: { return str; } } return str; } asl_string_t * asl_string_append(asl_string_t *str, const char *app) { return asl_string_append_internal(str, app, 0); } asl_string_t * asl_string_append_asl_key(asl_string_t *str, const char *app) { return asl_string_append_internal(str, app, 1); } asl_string_t * asl_string_append_op(asl_string_t *str, uint32_t op) { char opstr[8]; uint32_t i; if (str == NULL) return NULL; if (op == ASL_QUERY_OP_NULL) { return asl_string_append_char_no_encoding(str, '.'); } i = 0; if (op & ASL_QUERY_OP_CASEFOLD) opstr[i++] = 'C'; if (op & ASL_QUERY_OP_REGEX) opstr[i++] = 'R'; if (op & ASL_QUERY_OP_NUMERIC) opstr[i++] = 'N'; if (op & ASL_QUERY_OP_PREFIX) { if (op & ASL_QUERY_OP_SUFFIX) opstr[i++] = 'S'; else opstr[i++] = 'A'; } if (op & ASL_QUERY_OP_SUFFIX) opstr[i++] = 'Z'; switch (op & ASL_QUERY_OP_TRUE) { case ASL_QUERY_OP_EQUAL: opstr[i++] = '='; break; case ASL_QUERY_OP_GREATER: opstr[i++] = '>'; break; case ASL_QUERY_OP_GREATER_EQUAL: opstr[i++] = '>'; opstr[i++] = '='; break; case ASL_QUERY_OP_LESS: opstr[i++] = '<'; break; case ASL_QUERY_OP_LESS_EQUAL: opstr[i++] = '<'; opstr[i++] = '='; break; case ASL_QUERY_OP_NOT_EQUAL: opstr[i++] = '!'; break; case ASL_QUERY_OP_TRUE: opstr[i++] = 'T'; break; default: break; } if (i == 0) { return asl_string_append_char_no_encoding(str, '.'); } opstr[i] = '\0'; return asl_string_append_no_encoding(str, opstr); } asl_string_t * asl_string_append_xml_tag(asl_string_t *str, const char *tag, const char *s) { asl_string_append_no_encoding(str, "\t\t<"); asl_string_append_no_encoding(str, tag); asl_string_append_no_encoding(str, ">"); asl_string_append_no_encoding(str, s); asl_string_append_no_encoding(str, "\n"); return str; }