Line data Source code
1 : /* tlv.c - Tag-Length-Value Utilities
2 : * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * This file is free software; you can redistribute it and/or modify
7 : * it under the terms of either
8 : *
9 : * - the GNU Lesser General Public License as published by the Free
10 : * Software Foundation; either version 3 of the License, or (at
11 : * your option) any later version.
12 : *
13 : * or
14 : *
15 : * - the GNU General Public License as published by the Free
16 : * Software Foundation; either version 2 of the License, or (at
17 : * your option) any later version.
18 : *
19 : * or both in parallel, as here.
20 : *
21 : * This file is distributed in the hope that it will be useful,
22 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 : * GNU General Public License for more details.
25 : *
26 : * You should have received a copy of the GNU General Public License
27 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
28 : */
29 :
30 : #include <config.h>
31 :
32 : #include <stdio.h>
33 : #include <stdlib.h>
34 : #include <string.h>
35 : #include <assert.h>
36 :
37 : #if GNUPG_MAJOR_VERSION == 1
38 : #define GPG_ERR_EOF (-1)
39 : #define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/
40 : #define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/
41 : typedef int gpg_error_t;
42 : #define gpg_make_err(x,n) (n)
43 : #else
44 : #include <gpg-error.h>
45 : #endif
46 :
47 : #include "util.h"
48 : #include "tlv.h"
49 :
50 : static const unsigned char *
51 0 : do_find_tlv (const unsigned char *buffer, size_t length,
52 : int tag, size_t *nbytes, int nestlevel)
53 : {
54 0 : const unsigned char *s = buffer;
55 0 : size_t n = length;
56 : size_t len;
57 : int this_tag;
58 : int composite;
59 :
60 : for (;;)
61 : {
62 0 : buffer = s;
63 0 : if (n < 2)
64 0 : return NULL; /* Buffer definitely too short for tag and length. */
65 0 : if (!*s || *s == 0xff)
66 : { /* Skip optional filler between TLV objects. */
67 0 : s++;
68 0 : n--;
69 0 : continue;
70 : }
71 0 : composite = !!(*s & 0x20);
72 0 : if ((*s & 0x1f) == 0x1f)
73 : { /* more tag bytes to follow */
74 0 : s++;
75 0 : n--;
76 0 : if (n < 2)
77 0 : return NULL; /* buffer definitely too short for tag and length. */
78 0 : if ((*s & 0x1f) == 0x1f)
79 0 : return NULL; /* We support only up to 2 bytes. */
80 0 : this_tag = (s[-1] << 8) | (s[0] & 0x7f);
81 : }
82 : else
83 0 : this_tag = s[0];
84 0 : len = s[1];
85 0 : s += 2; n -= 2;
86 0 : if (len < 0x80)
87 : ;
88 0 : else if (len == 0x81)
89 : { /* One byte length follows. */
90 0 : if (!n)
91 0 : return NULL; /* we expected 1 more bytes with the length. */
92 0 : len = s[0];
93 0 : s++; n--;
94 : }
95 0 : else if (len == 0x82)
96 : { /* Two byte length follows. */
97 0 : if (n < 2)
98 0 : return NULL; /* We expected 2 more bytes with the length. */
99 0 : len = ((size_t)s[0] << 8) | s[1];
100 0 : s += 2; n -= 2;
101 : }
102 : else
103 0 : return NULL; /* APDU limit is 65535, thus it does not make
104 : sense to assume longer length fields. */
105 :
106 0 : if (composite && nestlevel < 100)
107 : { /* Dive into this composite DO after checking for a too deep
108 : nesting. */
109 : const unsigned char *tmp_s;
110 : size_t tmp_len;
111 :
112 0 : tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
113 0 : if (tmp_s)
114 : {
115 0 : *nbytes = tmp_len;
116 0 : return tmp_s;
117 : }
118 : }
119 :
120 0 : if (this_tag == tag)
121 : {
122 0 : *nbytes = len;
123 0 : return s;
124 : }
125 0 : if (len > n)
126 0 : return NULL; /* Buffer too short to skip to the next tag. */
127 0 : s += len; n -= len;
128 0 : }
129 : }
130 :
131 :
132 : /* Locate a TLV encoded data object in BUFFER of LENGTH and
133 : return a pointer to value as well as its length in NBYTES. Return
134 : NULL if it was not found or if the object does not fit into the buffer. */
135 : const unsigned char *
136 0 : find_tlv (const unsigned char *buffer, size_t length,
137 : int tag, size_t *nbytes)
138 : {
139 : const unsigned char *p;
140 :
141 0 : p = do_find_tlv (buffer, length, tag, nbytes, 0);
142 0 : if (p && *nbytes > (length - (p-buffer)))
143 0 : p = NULL; /* Object longer than buffer. */
144 0 : return p;
145 : }
146 :
147 :
148 :
149 : /* Locate a TLV encoded data object in BUFFER of LENGTH and
150 : return a pointer to value as well as its length in NBYTES. Return
151 : NULL if it was not found. Note, that the function does not check
152 : whether the value fits into the provided buffer. */
153 : const unsigned char *
154 0 : find_tlv_unchecked (const unsigned char *buffer, size_t length,
155 : int tag, size_t *nbytes)
156 : {
157 0 : return do_find_tlv (buffer, length, tag, nbytes, 0);
158 : }
159 :
160 :
161 : /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
162 : and the length part from the TLV triplet. Update BUFFER and SIZE
163 : on success. */
164 : gpg_error_t
165 0 : parse_ber_header (unsigned char const **buffer, size_t *size,
166 : int *r_class, int *r_tag,
167 : int *r_constructed, int *r_ndef,
168 : size_t *r_length, size_t *r_nhdr)
169 : {
170 : int c;
171 : unsigned long tag;
172 0 : const unsigned char *buf = *buffer;
173 0 : size_t length = *size;
174 :
175 0 : *r_ndef = 0;
176 0 : *r_length = 0;
177 0 : *r_nhdr = 0;
178 :
179 : /* Get the tag. */
180 0 : if (!length)
181 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
182 0 : c = *buf++; length--; ++*r_nhdr;
183 :
184 0 : *r_class = (c & 0xc0) >> 6;
185 0 : *r_constructed = !!(c & 0x20);
186 0 : tag = c & 0x1f;
187 :
188 0 : if (tag == 0x1f)
189 : {
190 0 : tag = 0;
191 : do
192 : {
193 0 : tag <<= 7;
194 0 : if (!length)
195 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
196 0 : c = *buf++; length--; ++*r_nhdr;
197 0 : tag |= c & 0x7f;
198 :
199 : }
200 0 : while (c & 0x80);
201 : }
202 0 : *r_tag = tag;
203 :
204 : /* Get the length. */
205 0 : if (!length)
206 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
207 0 : c = *buf++; length--; ++*r_nhdr;
208 :
209 0 : if ( !(c & 0x80) )
210 0 : *r_length = c;
211 0 : else if (c == 0x80)
212 0 : *r_ndef = 1;
213 0 : else if (c == 0xff)
214 0 : return gpg_err_make (default_errsource, GPG_ERR_BAD_BER);
215 : else
216 : {
217 0 : unsigned long len = 0;
218 0 : int count = c & 0x7f;
219 :
220 0 : if (count > sizeof (len) || count > sizeof (size_t))
221 0 : return gpg_err_make (default_errsource, GPG_ERR_BAD_BER);
222 :
223 0 : for (; count; count--)
224 : {
225 0 : len <<= 8;
226 0 : if (!length)
227 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
228 0 : c = *buf++; length--; ++*r_nhdr;
229 0 : len |= c & 0xff;
230 : }
231 0 : *r_length = len;
232 : }
233 :
234 : /* Without this kludge some example certs can't be parsed. */
235 0 : if (*r_class == CLASS_UNIVERSAL && !*r_tag)
236 0 : *r_length = 0;
237 :
238 0 : *buffer = buf;
239 0 : *size = length;
240 0 : return 0;
241 : }
242 :
243 :
244 : /* FIXME: The following function should not go into this file but for
245 : now it is easier to keep it here. */
246 :
247 : /* Return the next token of an canonical encoded S-expression. BUF
248 : is the pointer to the S-expression and BUFLEN is a pointer to the
249 : length of this S-expression (used to validate the syntax). Both
250 : are updated to reflect the new position. The token itself is
251 : returned as a pointer into the original buffer at TOK and TOKLEN.
252 : If a parentheses is the next token, TOK will be set to NULL.
253 : TOKLEN is checked to be within the bounds. On error an error code
254 : is returned and no pointer is not guaranteed to point to
255 : a meaningful value. DEPTH should be initialized to 0 and will
256 : reflect on return the actual depth of the tree. To detect the end
257 : of the S-expression it is advisable to check DEPTH after a
258 : successful return.
259 :
260 : depth = 0;
261 : while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
262 : && depth)
263 : process_token (tok, toklen);
264 : if (err)
265 : handle_error ();
266 : */
267 : gpg_error_t
268 39 : parse_sexp (unsigned char const **buf, size_t *buflen,
269 : int *depth, unsigned char const **tok, size_t *toklen)
270 : {
271 : const unsigned char *s;
272 : size_t n, vlen;
273 :
274 39 : s = *buf;
275 39 : n = *buflen;
276 39 : *tok = NULL;
277 39 : *toklen = 0;
278 39 : if (!n)
279 0 : return *depth ? gpg_err_make (default_errsource, GPG_ERR_INV_SEXP) : 0;
280 39 : if (*s == '(')
281 : {
282 12 : s++; n--;
283 12 : (*depth)++;
284 12 : *buf = s;
285 12 : *buflen = n;
286 12 : return 0;
287 : }
288 27 : if (*s == ')')
289 : {
290 9 : if (!*depth)
291 0 : return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
292 9 : *toklen = 1;
293 9 : s++; n--;
294 9 : (*depth)--;
295 9 : *buf = s;
296 9 : *buflen = n;
297 9 : return 0;
298 : }
299 42 : for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
300 24 : vlen = vlen*10 + (*s - '0');
301 18 : if (!n || *s != ':')
302 0 : return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
303 18 : s++; n--;
304 18 : if (vlen > n)
305 0 : return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
306 18 : *tok = s;
307 18 : *toklen = vlen;
308 18 : s += vlen;
309 18 : n -= vlen;
310 18 : *buf = s;
311 18 : *buflen = n;
312 18 : return 0;
313 : }
|