1/* 2 * Copyright (c) 2007-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <ctype.h> 25#include <stdlib.h> 26#include <stdio.h> 27#include <asl.h> 28#include <string.h> 29#include <mach/kern_return.h> 30#include <mach/mach_init.h> 31#include <mach/mach_vm.h> 32#include <mach/vm_map.h> 33#include <mach/vm_param.h> 34#include <libkern/OSAtomic.h> 35#include <asl_string.h> 36#include <asl_private.h> 37 38#define ASL_STRING_QUANTUM 256 39static const char *cvis_7_13 = "abtnvfr"; 40 41asl_string_t * 42asl_string_new(uint32_t encoding) 43{ 44 asl_string_t *str = (asl_string_t *)calloc(1, sizeof(asl_string_t)); 45 if (str == NULL) return NULL; 46 47 str->asl_type = ASL_TYPE_STRING; 48 str->refcount = 1; 49 50 str->encoding = encoding; 51 str->delta = ASL_STRING_QUANTUM; 52 if (encoding & ASL_STRING_VM) str->delta = PAGE_SIZE; 53 str->bufsize = 0; 54 str->cursor = 0; 55 56 if (encoding & ASL_STRING_LEN) asl_string_append_no_encoding(str, " 0 "); 57 return str; 58} 59 60asl_string_t * 61asl_string_retain(asl_string_t *str) 62{ 63 if (str == NULL) return NULL; 64 65 OSAtomicIncrement32Barrier(&(str->refcount)); 66 return str; 67} 68 69void 70asl_string_release(asl_string_t *str) 71{ 72 if (str == NULL) return; 73 if (OSAtomicDecrement32Barrier(&(str->refcount)) != 0) return; 74 75 if (str->encoding & ASL_STRING_VM) 76 { 77 vm_deallocate(mach_task_self(), (vm_address_t)str->buf, str->bufsize); 78 } 79 else 80 { 81 free(str->buf); 82 } 83 84 free(str); 85} 86 87char * 88asl_string_release_return_bytes(asl_string_t *str) 89{ 90 char *out; 91 if (str == NULL) return NULL; 92 93 if (OSAtomicDecrement32Barrier(&(str->refcount)) != 0) 94 { 95 /* string is still retained - copy buf */ 96 if (str->encoding & ASL_STRING_VM) 97 { 98 if (str->bufsize == 0) return NULL; 99 100 vm_address_t new = 0; 101 kern_return_t kstatus = vm_allocate(mach_task_self(), &new, str->bufsize, TRUE); 102 if (kstatus != KERN_SUCCESS) return NULL; 103 104 memcpy((void *)new, str->buf, str->bufsize); 105 return (char *)new; 106 } 107 else 108 { 109 if (str->cursor == 0) return NULL; 110 return strdup(str->buf); 111 } 112 } 113 114 out = str->buf; 115 free(str); 116 return out; 117} 118 119char * 120asl_string_bytes(asl_string_t *str) 121{ 122 if (str == NULL) return NULL; 123 return str->buf; 124} 125 126/* length includes trailing nul */ 127size_t 128asl_string_length(asl_string_t *str) 129{ 130 if (str == NULL) return 0; 131 if (str->cursor == 0) return 0; 132 133 return str->cursor + 1; 134} 135 136size_t 137asl_string_allocated_size(asl_string_t *str) 138{ 139 if (str == NULL) return 0; 140 return str->bufsize; 141} 142 143static int 144_asl_string_grow(asl_string_t *str, size_t len) 145{ 146 size_t newlen = 0; 147 148 if (str == NULL) return -1; 149 if (len == 0) return 0; 150 151 if (str->bufsize == 0) 152 { 153 newlen = ((len + str->delta - 1) / str->delta) * str->delta; 154 } 155 else 156 { 157 /* used size is (str->cursor + 1) including tailiing nul */ 158 if (len <= (str->bufsize - (str->cursor + 1))) return 0; 159 160 /* really this is ((str->cursor + 1) + len + (str->delta - 1)) */ 161 newlen = ((str->cursor + len + str->delta) / str->delta) * str->delta; 162 } 163 164 if (str->encoding & ASL_STRING_VM) 165 { 166 kern_return_t kstatus; 167 vm_address_t new = 0; 168 169 kstatus = vm_allocate(mach_task_self(), &new, newlen, TRUE); 170 if (kstatus != KERN_SUCCESS) 171 { 172 new = 0; 173 newlen = 0; 174 return -1; 175 } 176 177 if (str->buf != NULL) 178 { 179 memcpy((void *)new, str->buf, str->bufsize); 180 vm_deallocate(mach_task_self(), (vm_address_t)str->buf, str->bufsize); 181 } 182 183 str->buf = (char *)new; 184 str->bufsize = newlen; 185 } 186 else 187 { 188 str->buf = reallocf(str->buf, newlen); 189 if (str->buf == NULL) 190 { 191 str->cursor = 0; 192 str->bufsize = 0; 193 return -1; 194 } 195 196 str->bufsize = newlen; 197 } 198 199 return 0; 200} 201 202asl_string_t * 203asl_string_append_char_no_encoding(asl_string_t *str, const char c) 204{ 205 size_t len; 206 207 if (str == NULL) return NULL; 208 209 len = 1; 210 if (str->bufsize == 0) len++; 211 212 if (_asl_string_grow(str, len) < 0) return str; 213 214 str->buf[str->cursor] = c; 215 str->cursor++; 216 str->buf[str->cursor] = '\0'; 217 218 if (str->encoding & ASL_STRING_LEN) 219 { 220 char tmp[11]; 221 snprintf(tmp, sizeof(tmp), "%10lu", str->cursor - 10); 222 memcpy(str->buf, tmp, 10); 223 } 224 225 return str; 226} 227 228asl_string_t * 229asl_string_append_no_encoding(asl_string_t *str, const char *app) 230{ 231 size_t len, applen; 232 233 if (str == NULL) return NULL; 234 if (app == NULL) return str; 235 236 applen = strlen(app); 237 len = applen; 238 if (str->bufsize == 0) len++; 239 240 if (_asl_string_grow(str, len) < 0) return str; 241 242 memcpy(str->buf + str->cursor, app, applen); 243 244 str->cursor += applen; 245 str->buf[str->cursor] = '\0'; 246 247 if (str->encoding & ASL_STRING_LEN) 248 { 249 char tmp[11]; 250 snprintf(tmp, sizeof(tmp), "%10lu", str->cursor - 10); 251 memcpy(str->buf, tmp, 10); 252 } 253 254 return str; 255} 256 257static asl_string_t * 258asl_string_append_internal(asl_string_t *str, const char *app, int encode_space) 259{ 260 uint8_t x; 261 const char *p; 262 263 if (str == NULL) return NULL; 264 if (app == NULL) return str; 265 266 switch (str->encoding & ASL_ENCODE_MASK) 267 { 268 case ASL_ENCODE_NONE: 269 { 270 return asl_string_append_no_encoding(str, app); 271 } 272 case ASL_ENCODE_SAFE: 273 { 274 /* minor encoding to reduce the likelyhood of spoof attacks */ 275 const char *p; 276 277 for (p = app; *p != '\0'; p++) 278 { 279 if ((*p == 10) || (*p == 13)) 280 { 281 asl_string_append_no_encoding(str, "\n\t"); 282 } 283 else if (*p == 8) 284 { 285 asl_string_append_no_encoding(str, "^H"); 286 } 287 else 288 { 289 asl_string_append_char_no_encoding(str, *p); 290 } 291 } 292 293 return str; 294 } 295 case ASL_ENCODE_ASL: 296 { 297 for (p = app; *p != '\0'; p++) 298 { 299 int meta = 0; 300 301 x = *p; 302 303 /* Meta chars get \M prefix */ 304 if (x >= 128) 305 { 306 /* except meta-space, which is \240 */ 307 if (x == 160) 308 { 309 asl_string_append_no_encoding(str, "\\240"); 310 continue; 311 } 312 313 asl_string_append_no_encoding(str, "\\M"); 314 x &= 0x7f; 315 meta = 1; 316 } 317 318 /* space is either ' ' or \s */ 319 if (x == 32) 320 { 321 if (encode_space == 0) 322 { 323 asl_string_append_char_no_encoding(str, ' '); 324 continue; 325 } 326 327 asl_string_append_no_encoding(str, "\\s"); 328 continue; 329 } 330 331 /* \ is escaped */ 332 if ((meta == 0) && (x == 92)) 333 { 334 asl_string_append_no_encoding(str, "\\\\"); 335 continue; 336 } 337 338 /* [ and ] are escaped in ASL encoding */ 339 if ((str->encoding & ASL_ENCODE_ASL) && (meta == 0) && ((*p == 91) || (*p == 93))) 340 { 341 if (*p == '[') asl_string_append_no_encoding(str, "\\["); 342 else asl_string_append_no_encoding(str, "\\]"); 343 continue; 344 } 345 346 /* DEL is \^? */ 347 if (x == 127) 348 { 349 if (meta == 0) 350 { 351 asl_string_append_char_no_encoding(str, '\\'); 352 } 353 354 asl_string_append_no_encoding(str, "^?"); 355 continue; 356 } 357 358 /* 33-126 are printable (add a '-' prefix for meta) */ 359 if ((x >= 33) && (x <= 126)) 360 { 361 if (meta == 1) 362 { 363 asl_string_append_char_no_encoding(str, '-'); 364 } 365 366 asl_string_append_char_no_encoding(str, x); 367 continue; 368 } 369 370 /* non-meta BEL, BS, HT, NL, VT, NP, CR (7-13) are \a, \b, \t, \n, \v, \f, and \r */ 371 if ((meta == 0) && (x >= 7) && (x <= 13)) 372 { 373 asl_string_append_char_no_encoding(str, '\\'); 374 asl_string_append_char_no_encoding(str, cvis_7_13[x - 7]); 375 continue; 376 } 377 378 /* 0 - 31 are ^@ - ^_ (non-meta get a leading \) */ 379 if (x <= 31) 380 { 381 if (meta == 0) 382 { 383 asl_string_append_char_no_encoding(str, '\\'); 384 } 385 386 asl_string_append_char_no_encoding(str, '^'); 387 asl_string_append_char_no_encoding(str, 64 + x); 388 continue; 389 } 390 391 asl_string_append_char_no_encoding(str, x); 392 } 393 394 return str; 395 } 396 case ASL_ENCODE_XML: 397 { 398 for (p = app; *p != '\0'; p++) 399 { 400 x = *p; 401 402 if (x == '&') 403 { 404 asl_string_append_no_encoding(str, "&"); 405 } 406 else if (x == '<') 407 { 408 asl_string_append_no_encoding(str, "<"); 409 } 410 else if (x == '>') 411 { 412 asl_string_append_no_encoding(str, ">"); 413 } 414 else if (x == '"') 415 { 416 asl_string_append_no_encoding(str, """); 417 } 418 else if (x == '\'') 419 { 420 asl_string_append_no_encoding(str, "'"); 421 } 422 else if (iscntrl(x)) 423 { 424 char tmp[8]; 425 snprintf(tmp, sizeof(tmp), "&#x%02hhx;", x); 426 asl_string_append_no_encoding(str, tmp); 427 } 428 else 429 { 430 asl_string_append_char_no_encoding(str, x); 431 } 432 } 433 } 434 default: 435 { 436 return str; 437 } 438 } 439 440 return str; 441} 442 443asl_string_t * 444asl_string_append(asl_string_t *str, const char *app) 445{ 446 return asl_string_append_internal(str, app, 0); 447} 448 449asl_string_t * 450asl_string_append_asl_key(asl_string_t *str, const char *app) 451{ 452 return asl_string_append_internal(str, app, 1); 453} 454 455asl_string_t * 456asl_string_append_op(asl_string_t *str, uint32_t op) 457{ 458 char opstr[8]; 459 uint32_t i; 460 461 if (str == NULL) return NULL; 462 463 if (op == ASL_QUERY_OP_NULL) 464 { 465 return asl_string_append_char_no_encoding(str, '.'); 466 } 467 468 i = 0; 469 if (op & ASL_QUERY_OP_CASEFOLD) opstr[i++] = 'C'; 470 471 if (op & ASL_QUERY_OP_REGEX) opstr[i++] = 'R'; 472 473 if (op & ASL_QUERY_OP_NUMERIC) opstr[i++] = 'N'; 474 475 if (op & ASL_QUERY_OP_PREFIX) 476 { 477 if (op & ASL_QUERY_OP_SUFFIX) opstr[i++] = 'S'; 478 else opstr[i++] = 'A'; 479 } 480 if (op & ASL_QUERY_OP_SUFFIX) opstr[i++] = 'Z'; 481 482 switch (op & ASL_QUERY_OP_TRUE) 483 { 484 case ASL_QUERY_OP_EQUAL: 485 opstr[i++] = '='; 486 break; 487 case ASL_QUERY_OP_GREATER: 488 opstr[i++] = '>'; 489 break; 490 case ASL_QUERY_OP_GREATER_EQUAL: 491 opstr[i++] = '>'; 492 opstr[i++] = '='; 493 break; 494 case ASL_QUERY_OP_LESS: 495 opstr[i++] = '<'; 496 break; 497 case ASL_QUERY_OP_LESS_EQUAL: 498 opstr[i++] = '<'; 499 opstr[i++] = '='; 500 break; 501 case ASL_QUERY_OP_NOT_EQUAL: 502 opstr[i++] = '!'; 503 break; 504 case ASL_QUERY_OP_TRUE: 505 opstr[i++] = 'T'; 506 break; 507 default: 508 break; 509 } 510 511 if (i == 0) 512 { 513 return asl_string_append_char_no_encoding(str, '.'); 514 } 515 516 opstr[i] = '\0'; 517 return asl_string_append_no_encoding(str, opstr); 518} 519 520asl_string_t * 521asl_string_append_xml_tag(asl_string_t *str, const char *tag, const char *s) 522{ 523 asl_string_append_no_encoding(str, "\t\t<"); 524 asl_string_append_no_encoding(str, tag); 525 asl_string_append_no_encoding(str, ">"); 526 asl_string_append_internal(str, s, 0); 527 asl_string_append_no_encoding(str, "</"); 528 asl_string_append_no_encoding(str, tag); 529 asl_string_append_no_encoding(str, ">\n"); 530 return str; 531} 532 533