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 : 931 : 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 [ + + ]: 5863 : for (i = 0; i < tlv_len; i++) {
43 : : /* This TLV is skipped */
44 [ + + ]: 4936 : 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 [ + + ]: 4754 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND
51 [ + + ]: 152 : && buffer_type != SIMPLETLV_BOTH)
52 : : return -1;
53 : :
54 : 4750 : child_length = tlv[i].length;
55 [ + + ]: 4750 : 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 [ + + ]: 4750 : if (buffer_type & SIMPLETLV_TL) {
60 : : len += 1/*TAG*/;
61 [ + + ]: 4307 : if (child_length < 255)
62 : 4271 : len += 1;
63 : : else
64 : 36 : len += 3;
65 : : }
66 [ + + ]: 4750 : if (buffer_type & SIMPLETLV_VALUE) {
67 : 4308 : len += child_length;
68 : : }
69 : : }
70 : 927 : return len;
71 : : }
72 : :
73 : : static int
74 : 581 : 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 : 581 : 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 : 581 : expect_len = simpletlv_get_length(tlv, tlv_len, buffer_type);
83 [ + + + - ]: 581 : if (expect_len == 0 && newptr != NULL && out != NULL)
84 : 1 : *newptr = *out; /* Corner case for zero-length values */
85 [ + + ]: 581 : if (expect_len <= 0)
86 : : return expect_len;
87 : :
88 [ + + ]: 577 : if (outlen == 0 && out != NULL) {
89 : : /* allocate a new buffer */
90 : 380 : a = g_malloc(expect_len);
91 : : tmp = a;
92 : : tmp_len = expect_len;
93 [ + + + - ]: 197 : } else if ((int)outlen >= expect_len && out != NULL) {
94 : 194 : tmp = *out;
95 : 194 : tmp_len = outlen;
96 : : } else {
97 : : /* we can not fit the data */
98 : : return -1;
99 : : }
100 : 574 : p = tmp;
101 : : p_len = tmp_len;
102 [ + + ]: 3950 : for (i = 0; i < tlv_len; i++) {
103 : 3376 : size_t child_length = tlv[i].length;
104 : :
105 : : /* This TLV is skipped */
106 [ + + ]: 3376 : if (tlv[i].type == SIMPLETLV_TYPE_NONE)
107 : 91 : continue;
108 : :
109 [ + + ]: 3285 : 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 [ + + ]: 3285 : if (buffer_type & SIMPLETLV_TL) {
114 : 3065 : rv = simpletlv_put_tag(tlv[i].tag, child_length,
115 : : p, p_len, &newp);
116 [ - + ]: 3065 : if (rv < 0)
117 : 0 : goto failure;
118 : 3065 : p = newp;
119 : : }
120 [ + + ]: 3285 : if (buffer_type & SIMPLETLV_VALUE) {
121 [ + + ]: 3064 : if (tlv[i].type == SIMPLETLV_TYPE_LEAF) {
122 : 2918 : memcpy(p, tlv[i].value.value, tlv[i].length);
123 : 2918 : p += tlv[i].length;
124 : : } else {
125 : : /* recurse */
126 : 146 : rv = simpletlv_encode_internal(tlv[i].value.child,
127 : 146 : tlv[i].length, &p, p_len, &newp, buffer_type);
128 [ - + ]: 146 : if (rv < 0)
129 : 0 : goto failure;
130 : 146 : p = newp;
131 : : }
132 : : }
133 : 3285 : p_len = tmp_len - (p - tmp);
134 : : }
135 [ + + ]: 574 : if (newptr)
136 : 146 : *newptr = p;
137 [ + - ]: 574 : if (out)
138 : 574 : *out = tmp;
139 : 574 : return tmp_len - p_len;
140 : :
141 : 0 : failure:
142 : 0 : g_free(a);
143 : 0 : return -1;
144 : : }
145 : :
146 : : int
147 : 382 : simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
148 : : unsigned char **out, size_t outlen, unsigned char **newptr)
149 : : {
150 : 382 : return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
151 : : SIMPLETLV_BOTH);
152 : : }
153 : :
154 : : int
155 : 26 : simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
156 : : unsigned char **out, size_t outlen, unsigned char **newptr)
157 : : {
158 : 26 : return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
159 : : SIMPLETLV_TL);
160 : : }
161 : :
162 : : int
163 : 27 : simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
164 : : unsigned char **out, size_t outlen, unsigned char **newptr)
165 : : {
166 : 27 : return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
167 : : SIMPLETLV_VALUE);
168 : : }
169 : :
170 : :
171 : : /*
172 : : * Put a tag/length record to a file in Simple TLV based on the datalen
173 : : * content length.
174 : : */
175 : : int
176 : 3065 : simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
177 : : size_t outlen, unsigned char **ptr)
178 : : {
179 : : unsigned char *p = out;
180 : :
181 [ + - - + ]: 3065 : if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
182 : : return -1;
183 : :
184 : : /* tag is just number between 0x01 and 0xFE */
185 [ - + ]: 3065 : if (tag == 0x00 || tag == 0xff)
186 : : return -1;
187 : :
188 : 3065 : *p++ = tag; /* tag is single byte */
189 [ + + ]: 3065 : if (datalen < 0xff) {
190 : : /* short value up to 255 */
191 : 3047 : *p++ = (unsigned char)datalen; /* is in the second byte */
192 [ - + ]: 18 : } else if (datalen < 0xffff) {
193 : : /* longer values up to 65535 */
194 : 18 : *p++ = (unsigned char)0xff; /* first byte is 0xff */
195 : 18 : *p++ = (unsigned char)datalen & 0xff;
196 : 18 : *p++ = (unsigned char)(datalen >> 8) & 0xff; /* LE */
197 : : } else {
198 : : /* we can't store more than two bytes in Simple TLV */
199 : : return -1;
200 : : }
201 [ + - ]: 3065 : if (ptr != NULL)
202 : 3065 : *ptr = p;
203 : : return 0;
204 : : }
205 : :
206 : : /* Read the TL file and return appropriate tag and the length of associated
207 : : * content.
208 : : */
209 : : int
210 : 847 : simpletlv_read_tag(unsigned char **buf, size_t buflen, unsigned char *tag_out,
211 : : size_t *taglen)
212 : : {
213 : : size_t len;
214 : 847 : unsigned char *p = *buf;
215 : :
216 [ + + ]: 847 : if (buflen < 2) {
217 : 1 : *buf = p+buflen;
218 : 1 : return -1;
219 : : }
220 : :
221 : 846 : *tag_out = *p++;
222 : 846 : len = *p++;
223 [ + + ]: 846 : if (len == 0xff) {
224 : : /* don't crash on bad data */
225 [ + + ]: 3 : if (buflen < 4) {
226 : 1 : *taglen = 0;
227 : 1 : return -1;
228 : : }
229 : : /* skip two bytes (the size) */
230 : 2 : len = lebytes2ushort(p);
231 : 2 : p+=2;
232 : : }
233 : 845 : *taglen = len;
234 : 845 : *buf = p;
235 : 845 : return 0;
236 : : }
237 : :
238 : : /*
239 : : * Merges two structures into one, creating a new shallow copy of both
240 : : * of the structures.
241 : : * Resulting length is the sum of a_len and b_len arguments.
242 : : */
243 : : struct simpletlv_member *
244 : 93 : simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
245 : : const struct simpletlv_member *b, size_t b_len)
246 : : {
247 : : int offset;
248 : : struct simpletlv_member *r;
249 : 93 : size_t r_len = a_len + b_len;
250 : :
251 [ - + - - ]: 93 : r = g_new(struct simpletlv_member, r_len);
252 : :
253 : : /* the ugly way */
254 : 93 : offset = a_len * sizeof(struct simpletlv_member);
255 : 93 : memcpy(r, a, offset);
256 : 93 : memcpy(&r[a_len], b, b_len * sizeof(struct simpletlv_member));
257 : 93 : return r;
258 : : }
259 : :
260 : : void
261 : 287 : simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen)
262 : : {
263 : : size_t i;
264 [ + - ]: 287 : if (tlv == NULL)
265 : : return;
266 : :
267 [ + + ]: 2829 : for (i = 0; i < tlvlen; i++) {
268 [ + + ]: 2542 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
269 : 68 : simpletlv_free(tlv[i].value.child, tlv[i].length);
270 : : } else {
271 : 2474 : g_free(tlv[i].value.value);
272 : : }
273 : : }
274 : 287 : g_free(tlv);
275 : : }
276 : :
277 : : struct simpletlv_member *
278 : 219 : simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen)
279 : : {
280 : : size_t i = 0, j;
281 : : struct simpletlv_member *new = NULL;
282 : :
283 [ - + - - ]: 219 : new = g_new(struct simpletlv_member, tlvlen);
284 : :
285 [ + + ]: 923 : for (i = 0; i < tlvlen; i++) {
286 : 704 : new[i].type = tlv[i].type;
287 : 704 : new[i].tag = tlv[i].tag;
288 : 704 : new[i].length = tlv[i].length;
289 [ + + ]: 704 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
290 : 82 : new[i].value.child = simpletlv_clone(
291 : : tlv[i].value.child, tlv[i].length);
292 [ - + ]: 82 : if (new[i].value.child == NULL)
293 : 0 : goto failure;
294 : : } else {
295 : 622 : new[i].value.value = g_memdup2(tlv[i].value.value, tlv[i].length);
296 : : }
297 : : }
298 : : return new;
299 : :
300 : : failure:
301 [ # # ]: 0 : for (j = 0; j < i; j++) {
302 [ # # ]: 0 : if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
303 : 0 : simpletlv_free(new[j].value.child, new[j].length);
304 : : } else {
305 : 0 : g_free(new[j].value.value);
306 : : }
307 : : }
308 : 0 : g_free(new);
309 : 0 : return NULL;
310 : : }
311 : :
312 : : struct simpletlv_member *
313 : 5 : simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len)
314 : : {
315 : : unsigned char *p, *p_end;
316 : : unsigned char tag;
317 : : size_t vlen;
318 : 5 : GArray *tlv = g_array_new(FALSE, FALSE, sizeof(struct simpletlv_member));
319 : :
320 : 5 : p = data;
321 : 5 : p_end = p + data_len;
322 [ + + ]: 521 : while (p < p_end) {
323 : : struct simpletlv_member tlvp;
324 : :
325 : : /* we can return what was parsed successfully */
326 [ + + ]: 519 : if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
327 : : break;
328 : : }
329 [ + + ]: 517 : if (vlen > (size_t) (p_end - p)) {
330 : : break;
331 : : }
332 : :
333 : 516 : tlvp.tag = tag;
334 : 516 : tlvp.length = vlen;
335 : 516 : tlvp.value.value = g_memdup2(p, vlen);
336 : 516 : tlvp.type = SIMPLETLV_TYPE_LEAF;
337 : 516 : g_array_append_val(tlv, tlvp);
338 : :
339 : 516 : p += vlen;
340 : : }
341 : :
342 : 5 : *outtlv_len = tlv->len;
343 : 5 : return (struct simpletlv_member *)(void *)g_array_free(tlv, FALSE);
344 : : }
345 : :
346 : : /* vim: set ts=4 sw=4 tw=0 noet expandtab: */
|