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