Branch data Line data Source code
1 : : /*
2 : : * simpletlv.c: Simple TLV encoding and decoding functions
3 : : *
4 : : * Copyright (C) 2016 - 2018 Red Hat, Inc.
5 : : *
6 : : * Authors: Robert Relyea <rrelyea@redhat.com>
7 : : * Jakub Jelen <jjelen@redhat.com>
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Lesser General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Lesser General Public
20 : : * License along with this library; if not, write to the Free Software
21 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 : : */
23 : :
24 : : #include "config.h"
25 : :
26 : : #include <glib.h>
27 : : #include <stdio.h>
28 : : #include <string.h>
29 : : #include <ctype.h>
30 : : #include <stdlib.h>
31 : :
32 : : #include "simpletlv.h"
33 : : #include "common.h"
34 : :
35 : : int
36 : 938 : simpletlv_get_length(struct simpletlv_member *tlv, size_t tlv_len,
37 : : enum simpletlv_buffer_type buffer_type)
38 : : {
39 : : size_t i, len = 0;
40 : : int child_length;
41 : :
42 [ + + ]: 5879 : for (i = 0; i < tlv_len; i++) {
43 : : /* This TLV is skipped */
44 [ + + ]: 4945 : if (tlv[i].type == SIMPLETLV_TYPE_NONE)
45 : 182 : continue;
46 : :
47 : : /* We can not unambiguously split the buffers
48 : : * for recursive structures
49 : : */
50 [ + + ]: 4763 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND
51 [ + + ]: 152 : && buffer_type != SIMPLETLV_BOTH)
52 : : return -1;
53 : :
54 : 4759 : child_length = tlv[i].length;
55 [ + + ]: 4759 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
56 : 148 : child_length = simpletlv_get_length(tlv[i].value.child,
57 : : tlv[i].length, SIMPLETLV_BOTH);
58 : : }
59 [ + + ]: 4759 : if (buffer_type & SIMPLETLV_TL) {
60 : : len += 1/*TAG*/;
61 [ + + ]: 4313 : if (child_length < 255)
62 : 4277 : len += 1;
63 : : else
64 : 36 : len += 3;
65 : : }
66 [ + + ]: 4759 : if (buffer_type & SIMPLETLV_VALUE) {
67 : 4317 : len += child_length;
68 : : }
69 : : }
70 : 934 : return len;
71 : : }
72 : :
73 : : static int
74 : 588 : simpletlv_encode_internal(struct simpletlv_member *tlv, size_t tlv_len,
75 : : unsigned char **out, size_t outlen,
76 : : unsigned char **newptr, int buffer_type)
77 : : {
78 : 588 : unsigned char *tmp = NULL, *a = NULL, *p = NULL, *newp = NULL;
79 : : size_t tmp_len = 0, p_len, i;
80 : : int expect_len = 0, rv;
81 : :
82 : 588 : expect_len = simpletlv_get_length(tlv, tlv_len, buffer_type);
83 [ + + + - ]: 588 : if (expect_len == 0 && newptr != NULL && out != NULL)
84 : 1 : *newptr = *out; /* Corner case for zero-length values */
85 [ + + ]: 588 : if (expect_len <= 0)
86 : : return expect_len;
87 : :
88 [ + + ]: 584 : if (outlen == 0 && out != NULL) {
89 : : /* allocate a new buffer */
90 : 384 : a = g_malloc(expect_len);
91 : : tmp = a;
92 : : tmp_len = expect_len;
93 [ + + + - ]: 200 : } else if ((int)outlen >= expect_len && out != NULL) {
94 : 197 : tmp = *out;
95 : 197 : tmp_len = outlen;
96 : : } else {
97 : : /* we can not fit the data */
98 : : return -1;
99 : : }
100 : 581 : p = tmp;
101 : : p_len = tmp_len;
102 [ + + ]: 3963 : for (i = 0; i < tlv_len; i++) {
103 : 3385 : size_t child_length = tlv[i].length;
104 : :
105 : : /* This TLV is skipped */
106 [ + + ]: 3385 : if (tlv[i].type == SIMPLETLV_TYPE_NONE)
107 : 91 : continue;
108 : :
109 [ + + ]: 3294 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
110 : 146 : child_length = simpletlv_get_length(tlv[i].value.child,
111 : : tlv[i].length, SIMPLETLV_BOTH);
112 : : }
113 [ + + ]: 3294 : if (buffer_type & SIMPLETLV_TL) {
114 : 3071 : rv = simpletlv_put_tag(tlv[i].tag, child_length,
115 : : p, p_len, &newp);
116 [ - + ]: 3071 : if (rv < 0)
117 : 0 : goto failure;
118 : 3071 : p = newp;
119 : : }
120 [ + + ]: 3294 : if (buffer_type & SIMPLETLV_VALUE) {
121 [ + + ]: 3073 : if (tlv[i].type == SIMPLETLV_TYPE_LEAF) {
122 [ + + ]: 2927 : if (tlv[i].length > 0) {
123 [ + + ]: 2853 : if (tlv[i].value.value == NULL)
124 : 3 : goto failure;
125 : 2850 : memcpy(p, tlv[i].value.value, tlv[i].length);
126 : : }
127 : 2924 : p += tlv[i].length;
128 : : } else {
129 : : /* recurse */
130 : 146 : rv = simpletlv_encode_internal(tlv[i].value.child,
131 : 146 : tlv[i].length, &p, p_len, &newp, buffer_type);
132 [ - + ]: 146 : if (rv < 0)
133 : 0 : goto failure;
134 : 146 : p = newp;
135 : : }
136 : : }
137 : 3291 : p_len = tmp_len - (p - tmp);
138 : : }
139 [ + + ]: 578 : if (newptr)
140 : 147 : *newptr = p;
141 : : if (out)
142 : 578 : *out = tmp;
143 : 578 : return tmp_len - p_len;
144 : :
145 : 3 : failure:
146 : 3 : g_free(a);
147 : 3 : return -1;
148 : : }
149 : :
150 : : int
151 : 387 : simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
152 : : unsigned char **out, size_t outlen, unsigned char **newptr)
153 : : {
154 : 387 : return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
155 : : SIMPLETLV_BOTH);
156 : : }
157 : :
158 : : int
159 : 26 : simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
160 : : unsigned char **out, size_t outlen, unsigned char **newptr)
161 : : {
162 : 26 : return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
163 : : SIMPLETLV_TL);
164 : : }
165 : :
166 : : int
167 : 29 : simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
168 : : unsigned char **out, size_t outlen, unsigned char **newptr)
169 : : {
170 : 29 : return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
171 : : SIMPLETLV_VALUE);
172 : : }
173 : :
174 : :
175 : : /*
176 : : * Put a tag/length record to a file in Simple TLV based on the datalen
177 : : * content length.
178 : : */
179 : : int
180 : 3071 : simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
181 : : size_t outlen, unsigned char **ptr)
182 : : {
183 : : unsigned char *p = out;
184 : :
185 [ + - - + ]: 3071 : if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
186 : : return -1;
187 : :
188 : : /* tag is just number between 0x01 and 0xFE */
189 [ - + ]: 3071 : if (tag == 0x00 || tag == 0xff)
190 : : return -1;
191 : :
192 : 3071 : *p++ = tag; /* tag is single byte */
193 [ + + ]: 3071 : if (datalen < 0xff) {
194 : : /* short value up to 255 */
195 : 3053 : *p++ = (unsigned char)datalen; /* is in the second byte */
196 [ - + ]: 18 : } else if (datalen < 0xffff) {
197 : : /* longer values up to 65535 */
198 : 18 : *p++ = (unsigned char)0xff; /* first byte is 0xff */
199 : 18 : *p++ = (unsigned char)datalen & 0xff;
200 : 18 : *p++ = (unsigned char)(datalen >> 8) & 0xff; /* LE */
201 : : } else {
202 : : /* we can't store more than two bytes in Simple TLV */
203 : : return -1;
204 : : }
205 [ + - ]: 3071 : if (ptr != NULL)
206 : 3071 : *ptr = p;
207 : : return 0;
208 : : }
209 : :
210 : : /* Read the TL file and return appropriate tag and the length of associated
211 : : * content.
212 : : */
213 : : int
214 : 847 : simpletlv_read_tag(unsigned char **buf, size_t buflen, unsigned char *tag_out,
215 : : size_t *taglen)
216 : : {
217 : : size_t len;
218 : 847 : unsigned char *p = *buf;
219 : :
220 [ + + ]: 847 : if (buflen < 2) {
221 : 1 : *buf = p+buflen;
222 : 1 : return -1;
223 : : }
224 : :
225 : 846 : *tag_out = *p++;
226 : 846 : len = *p++;
227 [ + + ]: 846 : if (len == 0xff) {
228 : : /* don't crash on bad data */
229 [ + + ]: 3 : if (buflen < 4) {
230 : 1 : *taglen = 0;
231 : 1 : return -1;
232 : : }
233 : : /* skip two bytes (the size) */
234 : 2 : len = lebytes2ushort(p);
235 : 2 : p+=2;
236 : : }
237 : 845 : *taglen = len;
238 : 845 : *buf = p;
239 : 845 : return 0;
240 : : }
241 : :
242 : : /*
243 : : * Merges two structures into one, creating a new shallow copy of both
244 : : * of the structures.
245 : : * Resulting length is the sum of a_len and b_len arguments.
246 : : */
247 : : struct simpletlv_member *
248 : 93 : simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
249 : : const struct simpletlv_member *b, size_t b_len)
250 : : {
251 : : int offset;
252 : : struct simpletlv_member *r;
253 : 93 : size_t r_len = a_len + b_len;
254 : :
255 [ - + - - ]: 93 : r = g_new(struct simpletlv_member, r_len);
256 : :
257 : : /* the ugly way */
258 : 93 : offset = a_len * sizeof(struct simpletlv_member);
259 : 93 : memcpy(r, a, offset);
260 : 93 : memcpy(&r[a_len], b, b_len * sizeof(struct simpletlv_member));
261 : 93 : return r;
262 : : }
263 : :
264 : : void
265 : 287 : simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen)
266 : : {
267 : : size_t i;
268 [ + - ]: 287 : if (tlv == NULL)
269 : : return;
270 : :
271 [ + + ]: 2829 : for (i = 0; i < tlvlen; i++) {
272 [ + + ]: 2542 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
273 : 68 : simpletlv_free(tlv[i].value.child, tlv[i].length);
274 : : } else {
275 : 2474 : g_free(tlv[i].value.value);
276 : : }
277 : : }
278 : 287 : g_free(tlv);
279 : : }
280 : :
281 : : struct simpletlv_member *
282 : 219 : simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen)
283 : : {
284 : : size_t i = 0, j;
285 : : struct simpletlv_member *new = NULL;
286 : :
287 [ - + - - ]: 219 : new = g_new(struct simpletlv_member, tlvlen);
288 : :
289 [ + + ]: 923 : for (i = 0; i < tlvlen; i++) {
290 : 704 : new[i].type = tlv[i].type;
291 : 704 : new[i].tag = tlv[i].tag;
292 : 704 : new[i].length = tlv[i].length;
293 [ + + ]: 704 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
294 : 82 : new[i].value.child = simpletlv_clone(
295 : : tlv[i].value.child, tlv[i].length);
296 [ - + ]: 82 : if (new[i].value.child == NULL)
297 : 0 : goto failure;
298 : : } else {
299 : 622 : new[i].value.value = g_memdup2(tlv[i].value.value, tlv[i].length);
300 : : }
301 : : }
302 : : return new;
303 : :
304 : : failure:
305 [ # # ]: 0 : for (j = 0; j < i; j++) {
306 [ # # ]: 0 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
307 : 0 : simpletlv_free(new[j].value.child, new[j].length);
308 : : } else {
309 : 0 : g_free(new[j].value.value);
310 : : }
311 : : }
312 : 0 : g_free(new);
313 : 0 : return NULL;
314 : : }
315 : :
316 : : struct simpletlv_member *
317 : 5 : simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len)
318 : : {
319 : : unsigned char *p, *p_end;
320 : : unsigned char tag;
321 : : size_t vlen;
322 : 5 : GArray *tlv = g_array_new(FALSE, FALSE, sizeof(struct simpletlv_member));
323 : :
324 : 5 : p = data;
325 : 5 : p_end = p + data_len;
326 [ + + ]: 521 : while (p < p_end) {
327 : : struct simpletlv_member tlvp;
328 : :
329 : : /* we can return what was parsed successfully */
330 [ + + ]: 519 : if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
331 : : break;
332 : : }
333 [ + + ]: 517 : if (vlen > (size_t) (p_end - p)) {
334 : : break;
335 : : }
336 : :
337 : 516 : tlvp.tag = tag;
338 : 516 : tlvp.length = vlen;
339 : 516 : tlvp.value.value = g_memdup2(p, vlen);
340 : 516 : tlvp.type = SIMPLETLV_TYPE_LEAF;
341 : 516 : g_array_append_val(tlv, tlvp);
342 : :
343 : 516 : p += vlen;
344 : : }
345 : :
346 : 5 : *outtlv_len = tlv->len;
347 : 5 : return (struct simpletlv_member *)(void *)g_array_free(tlv, FALSE);
348 : : }
349 : :
350 : : /* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|