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, "&amp;");
405				}
406				else if (x == '<')
407				{
408					asl_string_append_no_encoding(str, "&lt;");
409				}
410				else if (x == '>')
411				{
412					asl_string_append_no_encoding(str, "&gt;");
413				}
414				else if (x == '"')
415				{
416					asl_string_append_no_encoding(str, "&quot;");
417				}
418				else if (x == '\'')
419				{
420					asl_string_append_no_encoding(str, "&apos;");
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