blobmsg: fix wrong payload len passed from blobmsg_check_array
[oweals/libubox.git] / blobmsg.c
1 /*
2  * Copyright (C) 2010-2012 Felix Fietkau <nbd@openwrt.org>
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 #include "blobmsg.h"
17
18 static const int blob_type[__BLOBMSG_TYPE_LAST] = {
19         [BLOBMSG_TYPE_INT8] = BLOB_ATTR_INT8,
20         [BLOBMSG_TYPE_INT16] = BLOB_ATTR_INT16,
21         [BLOBMSG_TYPE_INT32] = BLOB_ATTR_INT32,
22         [BLOBMSG_TYPE_INT64] = BLOB_ATTR_INT64,
23         [BLOBMSG_TYPE_DOUBLE] = BLOB_ATTR_DOUBLE,
24         [BLOBMSG_TYPE_STRING] = BLOB_ATTR_STRING,
25         [BLOBMSG_TYPE_UNSPEC] = BLOB_ATTR_BINARY,
26 };
27
28 bool blobmsg_check_attr(const struct blob_attr *attr, bool name)
29 {
30         return blobmsg_check_attr_len(attr, name, blob_raw_len(attr));
31 }
32
33 static bool blobmsg_check_name(const struct blob_attr *attr, size_t len, bool name)
34 {
35         char *limit = (char *) attr + len;
36         const struct blobmsg_hdr *hdr;
37
38         hdr = blob_data(attr);
39         if (name && !hdr->namelen)
40                 return false;
41
42         if ((char *) hdr->name + blobmsg_namelen(hdr) > limit)
43                 return false;
44
45         if (blobmsg_namelen(hdr) > (blob_len(attr) - sizeof(struct blobmsg_hdr)))
46                 return false;
47
48         if (hdr->name[blobmsg_namelen(hdr)] != 0)
49                 return false;
50
51         return true;
52 }
53
54 static const char* blobmsg_check_data(const struct blob_attr *attr, size_t len, size_t *data_len)
55 {
56         char *limit = (char *) attr + len;
57         const char *data;
58
59         *data_len = blobmsg_data_len(attr);
60         if (*data_len > blob_raw_len(attr))
61                 return NULL;
62
63         data = blobmsg_data(attr);
64         if (data + *data_len > limit)
65                 return NULL;
66
67         return data;
68 }
69
70 bool blobmsg_check_attr_len(const struct blob_attr *attr, bool name, size_t len)
71 {
72         const char *data;
73         size_t data_len;
74         int id;
75
76         if (len < sizeof(struct blob_attr))
77                 return false;
78
79         if (!blobmsg_check_name(attr, len, name))
80                 return false;
81
82         id = blob_id(attr);
83         if (id > BLOBMSG_TYPE_LAST)
84                 return false;
85
86         if (!blob_type[id])
87                 return true;
88
89         data = blobmsg_check_data(attr, len, &data_len);
90         if (!data)
91                 return false;
92
93         return blob_check_type(data, data_len, blob_type[id]);
94 }
95
96 int blobmsg_check_array(const struct blob_attr *attr, int type)
97 {
98         return blobmsg_check_array_len(attr, type, blobmsg_len(attr));
99 }
100
101 int blobmsg_check_array_len(const struct blob_attr *attr, int type, size_t len)
102 {
103         struct blob_attr *cur;
104         bool name;
105         int size = 0;
106
107         if (type > BLOBMSG_TYPE_LAST)
108                 return -1;
109
110         if (!blobmsg_check_attr_len(attr, false, len))
111                 return -1;
112
113         switch (blobmsg_type(attr)) {
114         case BLOBMSG_TYPE_TABLE:
115                 name = true;
116                 break;
117         case BLOBMSG_TYPE_ARRAY:
118                 name = false;
119                 break;
120         default:
121                 return -1;
122         }
123
124         __blobmsg_for_each_attr(cur, attr, len) {
125                 if (type != BLOBMSG_TYPE_UNSPEC && blobmsg_type(cur) != type)
126                         return -1;
127
128                 if (!blobmsg_check_attr_len(cur, name, len))
129                         return -1;
130
131                 size++;
132         }
133
134         return size;
135 }
136
137 bool blobmsg_check_attr_list(const struct blob_attr *attr, int type)
138 {
139         return blobmsg_check_array(attr, type) >= 0;
140 }
141
142 bool blobmsg_check_attr_list_len(const struct blob_attr *attr, int type, size_t len)
143 {
144         return blobmsg_check_array_len(attr, type, len) >= 0;
145 }
146
147 int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len,
148                         struct blob_attr **tb, void *data, unsigned int len)
149 {
150         struct blob_attr *attr;
151         int i = 0;
152
153         memset(tb, 0, policy_len * sizeof(*tb));
154         __blob_for_each_attr(attr, data, len) {
155                 if (policy[i].type != BLOBMSG_TYPE_UNSPEC &&
156                     blob_id(attr) != policy[i].type)
157                         continue;
158
159                 if (!blobmsg_check_attr_len(attr, false, len))
160                         return -1;
161
162                 if (tb[i])
163                         continue;
164
165                 tb[i++] = attr;
166                 if (i == policy_len)
167                         break;
168         }
169
170         return 0;
171 }
172
173
174 int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
175                   struct blob_attr **tb, void *data, unsigned int len)
176 {
177         struct blobmsg_hdr *hdr;
178         struct blob_attr *attr;
179         uint8_t *pslen;
180         int i;
181
182         memset(tb, 0, policy_len * sizeof(*tb));
183         if (!data || !len)
184                 return -EINVAL;
185         pslen = alloca(policy_len);
186         for (i = 0; i < policy_len; i++) {
187                 if (!policy[i].name)
188                         continue;
189
190                 pslen[i] = strlen(policy[i].name);
191         }
192
193         __blob_for_each_attr(attr, data, len) {
194                 hdr = blob_data(attr);
195                 for (i = 0; i < policy_len; i++) {
196                         if (!policy[i].name)
197                                 continue;
198
199                         if (policy[i].type != BLOBMSG_TYPE_UNSPEC &&
200                             blob_id(attr) != policy[i].type)
201                                 continue;
202
203                         if (blobmsg_namelen(hdr) != pslen[i])
204                                 continue;
205
206                         if (!blobmsg_check_attr_len(attr, true, len))
207                                 return -1;
208
209                         if (tb[i])
210                                 continue;
211
212                         if (strcmp(policy[i].name, (char *) hdr->name) != 0)
213                                 continue;
214
215                         tb[i] = attr;
216                 }
217         }
218
219         return 0;
220 }
221
222
223 static struct blob_attr *
224 blobmsg_new(struct blob_buf *buf, int type, const char *name, int payload_len, void **data)
225 {
226         struct blob_attr *attr;
227         struct blobmsg_hdr *hdr;
228         int attrlen, namelen;
229         char *pad_start, *pad_end;
230
231         if (!name)
232                 name = "";
233
234         namelen = strlen(name);
235         attrlen = blobmsg_hdrlen(namelen) + payload_len;
236         attr = blob_new(buf, type, attrlen);
237         if (!attr)
238                 return NULL;
239
240         attr->id_len |= be32_to_cpu(BLOB_ATTR_EXTENDED);
241         hdr = blob_data(attr);
242         hdr->namelen = cpu_to_be16(namelen);
243
244         memcpy(hdr->name, name, namelen);
245         hdr->name[namelen] = '\0';
246
247         pad_end = *data = blobmsg_data(attr);
248         pad_start = (char *) &hdr->name[namelen];
249         if (pad_start < pad_end)
250                 memset(pad_start, 0, pad_end - pad_start);
251
252         return attr;
253 }
254
255 static inline int
256 attr_to_offset(struct blob_buf *buf, struct blob_attr *attr)
257 {
258         return (char *)attr - (char *) buf->buf + BLOB_COOKIE;
259 }
260
261
262 void *
263 blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array)
264 {
265         struct blob_attr *head;
266         int type = array ? BLOBMSG_TYPE_ARRAY : BLOBMSG_TYPE_TABLE;
267         unsigned long offset = attr_to_offset(buf, buf->head);
268         void *data;
269
270         if (!name)
271                 name = "";
272
273         head = blobmsg_new(buf, type, name, 0, &data);
274         if (!head)
275                 return NULL;
276         blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blobmsg_hdrlen(strlen(name)));
277         buf->head = head;
278         return (void *)offset;
279 }
280
281 __attribute__((format(printf, 3, 0)))
282 int blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg)
283 {
284         va_list arg2;
285         char cbuf;
286         char *sbuf;
287         int len, ret;
288
289         va_copy(arg2, arg);
290         len = vsnprintf(&cbuf, sizeof(cbuf), format, arg2);
291         va_end(arg2);
292
293         sbuf = blobmsg_alloc_string_buffer(buf, name, len + 1);
294         if (!sbuf)
295                 return -1;
296         ret = vsprintf(sbuf, format, arg);
297         blobmsg_add_string_buffer(buf);
298
299         return ret;
300 }
301
302 __attribute__((format(printf, 3, 4)))
303 int blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...)
304 {
305         va_list ap;
306         int ret;
307
308         va_start(ap, format);
309         ret = blobmsg_vprintf(buf, name, format, ap);
310         va_end(ap);
311
312         return ret;
313 }
314
315 void *
316 blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int maxlen)
317 {
318         struct blob_attr *attr;
319         void *data_dest;
320
321         attr = blobmsg_new(buf, BLOBMSG_TYPE_STRING, name, maxlen, &data_dest);
322         if (!attr)
323                 return NULL;
324
325         blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blob_pad_len(attr));
326         blob_set_raw_len(attr, blob_raw_len(attr) - maxlen);
327
328         return data_dest;
329 }
330
331 void *
332 blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen)
333 {
334         struct blob_attr *attr = blob_next(buf->head);
335         int offset = attr_to_offset(buf, blob_next(buf->head)) + blob_pad_len(attr) - BLOB_COOKIE;
336         int required = maxlen - (buf->buflen - offset);
337
338         if (required <= 0)
339                 goto out;
340
341         if (!blob_buf_grow(buf, required))
342                 return NULL;
343         attr = blob_next(buf->head);
344
345 out:
346         return blobmsg_data(attr);
347 }
348
349 void
350 blobmsg_add_string_buffer(struct blob_buf *buf)
351 {
352         struct blob_attr *attr;
353         int len, attrlen;
354
355         attr = blob_next(buf->head);
356         len = strlen(blobmsg_data(attr)) + 1;
357
358         attrlen = blob_raw_len(attr) + len;
359         blob_set_raw_len(attr, attrlen);
360         blob_fill_pad(attr);
361
362         blob_set_raw_len(buf->head, blob_raw_len(buf->head) + blob_pad_len(attr));
363 }
364
365 int
366 blobmsg_add_field(struct blob_buf *buf, int type, const char *name,
367                   const void *data, unsigned int len)
368 {
369         struct blob_attr *attr;
370         void *data_dest;
371
372         attr = blobmsg_new(buf, type, name, len, &data_dest);
373         if (!attr)
374                 return -1;
375
376         if (len > 0)
377                 memcpy(data_dest, data, len);
378
379         return 0;
380 }