1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <arpa/inet.h>
29
30#include <ctype.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <string.h>
34#include <syslog.h>
35
36#include <dbus/dbus.h>
37
38#include "../config.h"
39#include "dbus-dict.h"
40
41static dbus_bool_t
42append_sanitized_string(DBusMessageIter *iter, const char *value)
43{
44	dbus_bool_t ret;
45	int len = strlen(value);
46	char *sanitized_value = NULL;
47	int i;
48
49	for (i = 0; i < len; i++) {
50		if (isascii(value[i]) || isprint(value[i])) {
51			if (sanitized_value)
52				sanitized_value[i] = value[i];
53		} else {
54			if (sanitized_value == NULL) {
55				sanitized_value = malloc(len + 1);
56				if (sanitized_value == NULL) {
57					syslog(LOG_ERR, "DBus string parameter "
58					       "sanitization failed due to "
59					       "malloc failure");
60					return FALSE;
61				}
62				memcpy(sanitized_value, value, i);
63			}
64			sanitized_value[i] = '?';
65		}
66	}
67	if (sanitized_value) {
68		syslog(LOG_ERR, "DBus string parameter sanitization"
69                       " was invoked");
70		sanitized_value[i] = '\0';
71		ret = dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
72	            &sanitized_value);
73
74		free(sanitized_value);
75	} else {
76		ret = dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
77	            &value);
78	}
79
80	return ret;
81}
82
83static int
84append_config_value(DBusMessageIter *entry, int type,
85    const char *data)
86{
87	int retval;
88	DBusMessageIter var;
89	unsigned char byte;
90	dbus_uint16_t u16;
91	dbus_uint32_t u32;
92	dbus_int16_t i16;
93	dbus_int32_t i32;
94	struct in_addr in;
95
96	retval = -1;
97	switch (type) {
98	case DBUS_TYPE_BOOLEAN:
99		if (*data == '0' || *data == '\0')
100			u32 = 0;
101		else
102			u32 = 1;
103		dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
104		    DBUS_TYPE_BOOLEAN_AS_STRING, &var);
105		if (dbus_message_iter_append_basic(&var,
106			DBUS_TYPE_BOOLEAN, &u32))
107			retval = 0;
108		break;
109	case DBUS_TYPE_BYTE:
110		byte = strtoul(data, NULL, 0);
111		dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
112		    DBUS_TYPE_BYTE_AS_STRING, &var);
113		if (dbus_message_iter_append_basic(&var, DBUS_TYPE_BYTE,
114			&byte))
115			retval = 0;
116		break;
117	case DBUS_TYPE_STRING:
118		dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
119		    DBUS_TYPE_STRING_AS_STRING, &var);
120		if (append_sanitized_string(&var, data))
121			retval = 0;
122		break;
123	case DBUS_TYPE_INT16:
124		i16 = strtol(data, NULL, 0);
125		dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
126		    DBUS_TYPE_INT16_AS_STRING, &var);
127		if (dbus_message_iter_append_basic(&var,
128			DBUS_TYPE_INT16, &i16))
129			retval = 0;
130		break;
131	case DBUS_TYPE_UINT16:
132		u16 = strtoul(data, NULL, 0);
133		dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
134		    DBUS_TYPE_UINT16_AS_STRING, &var);
135		if (dbus_message_iter_append_basic(&var,
136			DBUS_TYPE_UINT16, &u16))
137			retval = 0;
138		break;
139	case DBUS_TYPE_INT32:
140		i32 = strtol(data, NULL, 0);
141		dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
142		    DBUS_TYPE_INT32_AS_STRING, &var);
143		if (dbus_message_iter_append_basic(&var,
144			DBUS_TYPE_INT32, &i32))
145			retval = 0;
146		break;
147	case DBUS_TYPE_UINT32:
148		if (strchr(data, '.') != NULL && inet_aton(data, &in) == 1)
149			u32 = in.s_addr;
150		else
151			u32 = strtoul(data, NULL, 0);
152		dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
153		    DBUS_TYPE_UINT32_AS_STRING, &var);
154		if (dbus_message_iter_append_basic(&var,
155			DBUS_TYPE_UINT32, &u32))
156			retval = 0;
157		break;
158	default:
159		retval = 1;
160		break;
161	}
162	if (retval == 0)
163		dbus_message_iter_close_container(entry, &var);
164	else if (retval == 1)
165		retval = 0;
166
167	return retval;
168}
169
170static int
171append_config_byte_array(DBusMessageIter *entry, const char *data)
172{
173	DBusMessageIter var, array;
174	dbus_bool_t ok = TRUE;
175	uint8_t u8, u8_2;
176	size_t len;
177	const char *it, *end;
178	const char *tsa, *ts;
179
180	tsa = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
181	ts = DBUS_TYPE_BYTE_AS_STRING;
182
183	dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT, tsa, &var);
184	dbus_message_iter_open_container(&var, DBUS_TYPE_ARRAY, ts, &array);
185
186	len = strlen(data);
187	it = data;
188	end = data + len;
189
190	/* "a12" is treated as "0a12" */
191	if (len & 1) {
192		ok = (sscanf(it++, "%1hhx", &u8) == 1) &&
193			dbus_message_iter_append_basic(&array, DBUS_TYPE_BYTE,
194						       &u8);
195	}
196
197	while (ok && it < end) {
198		/* sscanf("1z", "%2hhx", &u8) will store 0x01 in u8 and
199		 * will return 1 */
200		ok = (sscanf(it++, "%1hhx", &u8) == 1) &&
201			(sscanf(it++, "%1hhx", &u8_2) == 1);
202		if (!ok)
203			break;
204
205		u8 = (u8 << 4) | u8_2;
206		ok = dbus_message_iter_append_basic(&array, DBUS_TYPE_BYTE, &u8);
207	}
208
209	dbus_message_iter_close_container(&var, &array);
210	dbus_message_iter_close_container(entry, &var);
211	return ok ? 0 : -1;
212}
213
214static int
215append_config_array(DBusMessageIter *entry, int type, const char *data)
216{
217	int retval;
218	char *ns, *p, *tok;
219	const char *tsa, *ts;
220	DBusMessageIter var, array;
221	dbus_bool_t ok;
222	dbus_uint32_t u32;
223	struct in_addr in;
224
225	if (type == DBUS_TYPE_BYTE)
226		return append_config_byte_array(entry, data);
227
228	switch (type) {
229	case DBUS_TYPE_STRING:
230		tsa = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
231		ts = DBUS_TYPE_STRING_AS_STRING;
232		break;
233	case DBUS_TYPE_UINT32:
234		tsa = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT32_AS_STRING;
235		ts = DBUS_TYPE_UINT32_AS_STRING;
236		break;
237	default:
238		return -1;
239	}
240
241	ns = p = strdup(data);
242	if (ns == NULL)
243		return -1;
244	retval = 0;
245
246	dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT, tsa, &var);
247	dbus_message_iter_open_container(&var, DBUS_TYPE_ARRAY, ts, &array);
248	while ((tok = strsep(&p, " ")) != NULL) {
249		if (*tok == '\0')
250			continue;
251		switch(type) {
252		case DBUS_TYPE_STRING:
253			ok = append_sanitized_string(&array, tok);
254			break;
255		case DBUS_TYPE_UINT32:
256			if (strchr(tok, '.') != NULL &&
257			    inet_aton(tok, &in) == 1)
258				u32 = in.s_addr;
259			else
260				u32 = strtoul(tok, NULL, 0);
261			ok = dbus_message_iter_append_basic(&array,
262			    DBUS_TYPE_UINT32, &u32);
263			break;
264		default:
265			ok = FALSE;
266			break;
267		}
268		if (!ok)
269			break;
270	}
271	dbus_message_iter_close_container(&var, &array);
272	dbus_message_iter_close_container(entry, &var);
273	free(ns);
274	return retval;
275}
276
277int
278dict_append_config_item(DBusMessageIter *iter, const struct o_dbus *op,
279    const char *data)
280{
281	int retval;
282	DBusMessageIter entry;
283
284	retval = 0;
285	if (*data == '\0')
286		return retval;
287	dbus_message_iter_open_container(iter,
288	    DBUS_TYPE_DICT_ENTRY,
289	    NULL,
290	    &entry);
291	append_sanitized_string(&entry, op->name);
292	if (op->type == DBUS_TYPE_ARRAY)
293		retval = append_config_array(&entry, op->sub_type, data);
294	else
295		retval = append_config_value(&entry, op->type, data);
296	dbus_message_iter_close_container(iter, &entry);
297	return retval;
298}
299