xref: /netbsd-src/common/lib/libprop/prop_data.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: prop_data.c,v 1.9 2007/08/30 12:23:54 joerg Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the NetBSD
21  *      Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <prop/prop_data.h>
40 #include "prop_object_impl.h"
41 
42 #if defined(_KERNEL)
43 #include <sys/systm.h>
44 #elif defined(_STANDALONE)
45 #include <sys/param.h>
46 #include <lib/libkern/libkern.h>
47 #else
48 #include <errno.h>
49 #include <limits.h>
50 #include <stdlib.h>
51 #endif
52 
53 struct _prop_data {
54 	struct _prop_object	pd_obj;
55 	union {
56 		void *		pdu_mutable;
57 		const void *	pdu_immutable;
58 	} pd_un;
59 #define	pd_mutable		pd_un.pdu_mutable
60 #define	pd_immutable		pd_un.pdu_immutable
61 	size_t			pd_size;
62 	int			pd_flags;
63 };
64 
65 #define	PD_F_NOCOPY		0x01
66 
67 _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
68 
69 _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
70 		    "property data container object")
71 
72 static int		_prop_data_free(prop_stack_t, prop_object_t *);
73 static bool	_prop_data_externalize(
74 				struct _prop_object_externalize_context *,
75 				void *);
76 static bool	_prop_data_equals(prop_object_t, prop_object_t,
77 				  void **, void **,
78 				  prop_object_t *, prop_object_t *);
79 
80 static const struct _prop_object_type _prop_object_type_data = {
81 	.pot_type	=	PROP_TYPE_DATA,
82 	.pot_free	=	_prop_data_free,
83 	.pot_extern	=	_prop_data_externalize,
84 	.pot_equals	=	_prop_data_equals,
85 };
86 
87 #define	prop_object_is_data(x)		\
88 	((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
89 
90 /* ARGSUSED */
91 static int
92 _prop_data_free(prop_stack_t stack, prop_object_t *obj)
93 {
94 	prop_data_t pd = *obj;
95 
96 	if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
97 	    	_PROP_FREE(pd->pd_mutable, M_PROP_DATA);
98 	_PROP_POOL_PUT(_prop_data_pool, pd);
99 
100 	return (_PROP_OBJECT_FREE_DONE);
101 }
102 
103 static const char _prop_data_base64[] =
104     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
105 static const char _prop_data_pad64 = '=';
106 
107 static bool
108 _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
109 {
110 	prop_data_t pd = v;
111 	size_t i, srclen;
112 	const uint8_t *src;
113 	uint8_t output[4];
114 	uint8_t input[3];
115 
116 	if (pd->pd_size == 0)
117 		return (_prop_object_externalize_empty_tag(ctx, "data"));
118 
119 	if (_prop_object_externalize_start_tag(ctx, "data") == false)
120 		return (false);
121 
122 	for (src = pd->pd_immutable, srclen = pd->pd_size;
123 	     srclen > 2; srclen -= 3) {
124 		input[0] = *src++;
125 		input[1] = *src++;
126 		input[2] = *src++;
127 
128 		output[0] = (uint32_t)input[0] >> 2;
129 		output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
130 		    ((uint32_t)input[1] >> 4);
131 		output[2] = ((u_int32_t)(input[1] & 0x0f) << 2) +
132 		    ((uint32_t)input[2] >> 6);
133 		output[3] = input[2] & 0x3f;
134 		_PROP_ASSERT(output[0] < 64);
135 		_PROP_ASSERT(output[1] < 64);
136 		_PROP_ASSERT(output[2] < 64);
137 		_PROP_ASSERT(output[3] < 64);
138 
139 		if (_prop_object_externalize_append_char(ctx,
140 				_prop_data_base64[output[0]]) == false ||
141 		    _prop_object_externalize_append_char(ctx,
142 		    		_prop_data_base64[output[1]]) == false ||
143 		    _prop_object_externalize_append_char(ctx,
144 		    		_prop_data_base64[output[2]]) == false ||
145 		    _prop_object_externalize_append_char(ctx,
146 		    		_prop_data_base64[output[3]]) == false)
147 			return (false);
148 	}
149 
150 	if (srclen != 0) {
151 		input[0] = input[1] = input[2] = '\0';
152 		for (i = 0; i < srclen; i++)
153 			input[i] = *src++;
154 
155 		output[0] = (uint32_t)input[0] >> 2;
156 		output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
157 		    ((uint32_t)input[1] >> 4);
158 		output[2] = ((u_int32_t)(input[1] & 0x0f) << 2) +
159 		    ((uint32_t)input[2] >> 6);
160 		_PROP_ASSERT(output[0] < 64);
161 		_PROP_ASSERT(output[1] < 64);
162 		_PROP_ASSERT(output[2] < 64);
163 
164 		if (_prop_object_externalize_append_char(ctx,
165 				_prop_data_base64[output[0]]) == false ||
166 		    _prop_object_externalize_append_char(ctx,
167 		    		_prop_data_base64[output[1]]) == false ||
168 		    _prop_object_externalize_append_char(ctx,
169 		    		srclen == 1 ? _prop_data_pad64
170 				: _prop_data_base64[output[2]]) == false ||
171 		    _prop_object_externalize_append_char(ctx,
172 		    		_prop_data_pad64) == false)
173 			return (false);
174 	}
175 
176 	if (_prop_object_externalize_end_tag(ctx, "data") == false)
177 		return (false);
178 
179 	return (true);
180 }
181 
182 /* ARGSUSED */
183 static bool
184 _prop_data_equals(prop_object_t v1, prop_object_t v2,
185     void **stored_pointer1, void **stored_pointer2,
186     prop_object_t *next_obj1, prop_object_t *next_obj2)
187 {
188 	prop_data_t pd1 = v1;
189 	prop_data_t pd2 = v2;
190 
191 	if (pd1 == pd2)
192 		return (_PROP_OBJECT_EQUALS_TRUE);
193 	if (pd1->pd_size != pd2->pd_size)
194 		return (_PROP_OBJECT_EQUALS_FALSE);
195 	if (pd1->pd_size == 0) {
196 		_PROP_ASSERT(pd1->pd_immutable == NULL);
197 		_PROP_ASSERT(pd2->pd_immutable == NULL);
198 		return (_PROP_OBJECT_EQUALS_TRUE);
199 	}
200 	if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
201 		return _PROP_OBJECT_EQUALS_TRUE;
202 	else
203 		return _PROP_OBJECT_EQUALS_FALSE;
204 }
205 
206 static prop_data_t
207 _prop_data_alloc(void)
208 {
209 	prop_data_t pd;
210 
211 	pd = _PROP_POOL_GET(_prop_data_pool);
212 	if (pd != NULL) {
213 		_prop_object_init(&pd->pd_obj, &_prop_object_type_data);
214 
215 		pd->pd_mutable = NULL;
216 		pd->pd_size = 0;
217 		pd->pd_flags = 0;
218 	}
219 
220 	return (pd);
221 }
222 
223 /*
224  * prop_data_create_data --
225  *	Create a data container that contains a copy of the data.
226  */
227 prop_data_t
228 prop_data_create_data(const void *v, size_t size)
229 {
230 	prop_data_t pd;
231 	void *nv;
232 
233 	pd = _prop_data_alloc();
234 	if (pd != NULL) {
235 		nv = _PROP_MALLOC(size, M_PROP_DATA);
236 		if (nv == NULL) {
237 			prop_object_release(pd);
238 			return (NULL);
239 		}
240 		memcpy(nv, v, size);
241 		pd->pd_mutable = nv;
242 		pd->pd_size = size;
243 	}
244 	return (pd);
245 }
246 
247 /*
248  * prop_data_create_data_nocopy --
249  *	Create an immutable data container that contains a refrence to the
250  *	provided external data.
251  */
252 prop_data_t
253 prop_data_create_data_nocopy(const void *v, size_t size)
254 {
255 	prop_data_t pd;
256 
257 	pd = _prop_data_alloc();
258 	if (pd != NULL) {
259 		pd->pd_immutable = v;
260 		pd->pd_size = size;
261 		pd->pd_flags |= PD_F_NOCOPY;
262 	}
263 	return (pd);
264 }
265 
266 /*
267  * prop_data_copy --
268  *	Copy a data container.  If the original data is external, then
269  *	the copy is also references the same external data.
270  */
271 prop_data_t
272 prop_data_copy(prop_data_t opd)
273 {
274 	prop_data_t pd;
275 
276 	if (! prop_object_is_data(opd))
277 		return (NULL);
278 
279 	pd = _prop_data_alloc();
280 	if (pd != NULL) {
281 		pd->pd_size = opd->pd_size;
282 		pd->pd_flags = opd->pd_flags;
283 		if (opd->pd_flags & PD_F_NOCOPY)
284 			pd->pd_immutable = opd->pd_immutable;
285 		else if (opd->pd_size != 0) {
286 			void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA);
287 			if (nv == NULL) {
288 				prop_object_release(pd);
289 				return (NULL);
290 			}
291 			memcpy(nv, opd->pd_immutable, opd->pd_size);
292 			pd->pd_mutable = nv;
293 		}
294 	}
295 	return (pd);
296 }
297 
298 /*
299  * prop_data_size --
300  *	Return the size of the data.
301  */
302 size_t
303 prop_data_size(prop_data_t pd)
304 {
305 
306 	if (! prop_object_is_data(pd))
307 		return (0);
308 
309 	return (pd->pd_size);
310 }
311 
312 /*
313  * prop_data_data --
314  *	Return a copy of the contents of the data container.
315  *	The data is allocated with the M_TEMP malloc type.
316  *	If the data container is empty, NULL is returned.
317  */
318 void *
319 prop_data_data(prop_data_t pd)
320 {
321 	void *v;
322 
323 	if (! prop_object_is_data(pd))
324 		return (NULL);
325 
326 	if (pd->pd_size == 0) {
327 		_PROP_ASSERT(pd->pd_immutable == NULL);
328 		return (NULL);
329 	}
330 
331 	_PROP_ASSERT(pd->pd_immutable != NULL);
332 
333 	v = _PROP_MALLOC(pd->pd_size, M_TEMP);
334 	if (v != NULL)
335 		memcpy(v, pd->pd_immutable, pd->pd_size);
336 
337 	return (v);
338 }
339 
340 /*
341  * prop_data_data_nocopy --
342  *	Return an immutable reference to the contents of the data
343  *	container.
344  */
345 const void *
346 prop_data_data_nocopy(prop_data_t pd)
347 {
348 
349 	if (! prop_object_is_data(pd))
350 		return (NULL);
351 
352 	_PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) ||
353 		     (pd->pd_size != 0 && pd->pd_immutable != NULL));
354 
355 	return (pd->pd_immutable);
356 }
357 
358 /*
359  * prop_data_equals --
360  *	Return true if two strings are equivalent.
361  */
362 bool
363 prop_data_equals(prop_data_t pd1, prop_data_t pd2)
364 {
365 	if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
366 		return (false);
367 
368 	return (prop_object_equals(pd1, pd2));
369 }
370 
371 /*
372  * prop_data_equals_data --
373  *	Return true if the contained data is equivalent to the specified
374  *	external data.
375  */
376 bool
377 prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
378 {
379 
380 	if (! prop_object_is_data(pd))
381 		return (false);
382 
383 	if (pd->pd_size != size)
384 		return (false);
385 	return (memcmp(pd->pd_immutable, v, size) == 0);
386 }
387 
388 static bool
389 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
390 			     uint8_t *target, size_t targsize, size_t *sizep,
391 			     const char **cpp)
392 {
393 	const char *src;
394 	size_t tarindex;
395 	int state, ch;
396 	const char *pos;
397 
398 	state = 0;
399 	tarindex = 0;
400 	src = ctx->poic_cp;
401 
402 	for (;;) {
403 		ch = (unsigned char) *src++;
404 		if (_PROP_EOF(ch))
405 			return (false);
406 		if (_PROP_ISSPACE(ch))
407 			continue;
408 		if (ch == '<') {
409 			src--;
410 			break;
411 		}
412 		if (ch == _prop_data_pad64)
413 			break;
414 
415 		pos = strchr(_prop_data_base64, ch);
416 		if (pos == NULL)
417 			return (false);
418 
419 		switch (state) {
420 		case 0:
421 			if (target) {
422 				if (tarindex >= targsize)
423 					return (false);
424 				target[tarindex] =
425 				    (uint8_t)((pos - _prop_data_base64) << 2);
426 			}
427 			state = 1;
428 			break;
429 
430 		case 1:
431 			if (target) {
432 				if (tarindex + 1 >= targsize)
433 					return (false);
434 				target[tarindex] |=
435 				    (uint32_t)(pos - _prop_data_base64) >> 4;
436 				target[tarindex + 1] =
437 				    (uint8_t)(((pos - _prop_data_base64) & 0xf)
438 				        << 4);
439 			}
440 			tarindex++;
441 			state = 2;
442 			break;
443 
444 		case 2:
445 			if (target) {
446 				if (tarindex + 1 >= targsize)
447 					return (false);
448 				target[tarindex] |=
449 				    (uint32_t)(pos - _prop_data_base64) >> 2;
450 				target[tarindex + 1] =
451 				    (uint8_t)(((pos - _prop_data_base64)
452 				        & 0x3) << 6);
453 			}
454 			tarindex++;
455 			state = 3;
456 			break;
457 
458 		case 3:
459 			if (target) {
460 				if (tarindex >= targsize)
461 					return (false);
462 				target[tarindex] |= (uint8_t)
463 				    (pos - _prop_data_base64);
464 			}
465 			tarindex++;
466 			state = 0;
467 			break;
468 
469 		default:
470 			_PROP_ASSERT(/*CONSTCOND*/0);
471 		}
472 	}
473 
474 	/*
475 	 * We are done decoding the Base64 characters.  Let's see if we
476 	 * ended up on a byte boundary and/or with unrecognized trailing
477 	 * characters.
478 	 */
479 	if (ch == _prop_data_pad64) {
480 		ch = (unsigned char) *src;	/* src already advanced */
481 		if (_PROP_EOF(ch))
482 			return (false);
483 		switch (state) {
484 		case 0:		/* Invalid = in first position */
485 		case 1:		/* Invalid = in second position */
486 			return (false);
487 
488 		case 2:		/* Valid, one byte of info */
489 			/* Skip whitespace */
490 			for (ch = (unsigned char) *src++;
491 			     ch != '<'; ch = (unsigned char) *src++) {
492 				if (_PROP_EOF(ch))
493 					return (false);
494 				if (!_PROP_ISSPACE(ch))
495 					break;
496 			}
497 			/* Make sure there is another trailing = */
498 			if (ch != _prop_data_pad64)
499 				return (false);
500 			ch = (unsigned char) *src;
501 			/* FALLTHROUGH */
502 
503 		case 3:		/* Valid, two bytes of info */
504 			/*
505 			 * We know this char is a =.  Is there anything but
506 			 * whitespace after it?
507 			 */
508 			for (ch = (unsigned char) *src++;
509 			     ch != '<'; ch = (unsigned char) *src++) {
510 				if (_PROP_EOF(ch))
511 					return (false);
512 				if (!_PROP_ISSPACE(ch))
513 					return (false);
514 			}
515 			/* back up to '<' */
516 			src--;
517 		}
518 	} else {
519 		/*
520 		 * We ended by seeing the end of the Base64 string.  Make
521 		 * sure there are no partial bytes lying around.
522 		 */
523 		if (state != 0)
524 			return (false);
525 	}
526 
527 	_PROP_ASSERT(*src == '<');
528 	if (sizep != NULL)
529 		*sizep = tarindex;
530 	if (cpp != NULL)
531 		*cpp = src;
532 
533 	return (true);
534 }
535 
536 /*
537  * _prop_data_internalize --
538  *	Parse a <data>...</data> and return the object created from the
539  *	external representation.
540  */
541 
542 /* strtoul is used for parsing, enforce. */
543 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
544 
545 /* ARGSUSED */
546 bool
547 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
548     struct _prop_object_internalize_context *ctx)
549 {
550 	prop_data_t data;
551 	uint8_t *buf;
552 	size_t len, alen;
553 
554 	/* We don't accept empty elements. */
555 	if (ctx->poic_is_empty_element)
556 		return (true);
557 
558 	/*
559 	 * If we got a "size" attribute, get the size of the data blob
560 	 * from that.  Otherwise, we have to figure it out from the base64.
561 	 */
562 	if (ctx->poic_tagattr != NULL) {
563 		char *cp;
564 
565 		if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
566 		    ctx->poic_tagattrval_len == 0)
567 			return (true);
568 
569 #ifndef _KERNEL
570 		errno = 0;
571 #endif
572 		len = strtoul(ctx->poic_tagattrval, &cp, 0);
573 #ifndef _KERNEL		/* XXX can't check for ERANGE in the kernel */
574 		if (len == ULONG_MAX && errno == ERANGE)
575 			return (true);
576 #endif
577 		if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
578 			return (true);
579 		_PROP_ASSERT(*cp == '\"');
580 	} else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
581 						NULL) == false)
582 		return (true);
583 
584 	/*
585 	 * Always allocate one extra in case we don't land on an even byte
586 	 * boundary during the decode.
587 	 */
588 	buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
589 	if (buf == NULL)
590 		return (true);
591 
592 	if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
593 					  &ctx->poic_cp) == false) {
594 		_PROP_FREE(buf, M_PROP_DATA);
595 		return (true);
596 	}
597 	if (alen != len) {
598 		_PROP_FREE(buf, M_PROP_DATA);
599 		return (true);
600 	}
601 
602 	if (_prop_object_internalize_find_tag(ctx, "data",
603 					      _PROP_TAG_TYPE_END) == false) {
604 		_PROP_FREE(buf, M_PROP_DATA);
605 		return (true);
606 	}
607 
608 	data = _prop_data_alloc();
609 	if (data == NULL) {
610 		_PROP_FREE(buf, M_PROP_DATA);
611 		return (true);
612 	}
613 
614 	data->pd_mutable = buf;
615 	data->pd_size = len;
616 
617 	*obj = data;
618 	return (true);
619 }
620