xref: /trueos/lib/libasl/asl_string.c (revision e0d5dc4e99cb0cffad102c7a22a16d828ef86ae5)
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, "&amp;");
409 				}
410 				else if (x == '<')
411 				{
412 					asl_string_append_no_encoding(str, "&lt;");
413 				}
414 				else if (x == '>')
415 				{
416 					asl_string_append_no_encoding(str, "&gt;");
417 				}
418 				else if (x == '"')
419 				{
420 					asl_string_append_no_encoding(str, "&quot;");
421 				}
422 				else if (x == '\'')
423 				{
424 					asl_string_append_no_encoding(str, "&apos;");
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