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 : if (n < 2)
63 0 : return NULL; /* Buffer definitely too short for tag and length. */
64 0 : if (!*s || *s == 0xff)
65 : { /* Skip optional filler between TLV objects. */
66 0 : s++;
67 0 : n--;
68 0 : continue;
69 : }
70 0 : composite = !!(*s & 0x20);
71 0 : if ((*s & 0x1f) == 0x1f)
72 : { /* more tag bytes to follow */
73 0 : s++;
74 0 : n--;
75 0 : if (n < 2)
76 0 : return NULL; /* buffer definitely too short for tag and length. */
77 0 : if ((*s & 0x1f) == 0x1f)
78 0 : return NULL; /* We support only up to 2 bytes. */
79 0 : this_tag = (s[-1] << 8) | (s[0] & 0x7f);
80 : }
81 : else
82 0 : this_tag = s[0];
83 0 : len = s[1];
84 0 : s += 2; n -= 2;
85 0 : if (len < 0x80)
86 : ;
87 0 : else if (len == 0x81)
88 : { /* One byte length follows. */
89 0 : if (!n)
90 0 : return NULL; /* we expected 1 more bytes with the length. */
91 0 : len = s[0];
92 0 : s++; n--;
93 : }
94 0 : else if (len == 0x82)
95 : { /* Two byte length follows. */
96 0 : if (n < 2)
97 0 : return NULL; /* We expected 2 more bytes with the length. */
98 0 : len = ((size_t)s[0] << 8) | s[1];
99 0 : s += 2; n -= 2;
100 : }
101 : else
102 0 : return NULL; /* APDU limit is 65535, thus it does not make
103 : sense to assume longer length fields. */
104 :
105 0 : if (composite && nestlevel < 100)
106 : { /* Dive into this composite DO after checking for a too deep
107 : nesting. */
108 : const unsigned char *tmp_s;
109 : size_t tmp_len;
110 :
111 0 : tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
112 0 : if (tmp_s)
113 : {
114 0 : *nbytes = tmp_len;
115 0 : return tmp_s;
116 : }
117 : }
118 :
119 0 : if (this_tag == tag)
120 : {
121 0 : *nbytes = len;
122 0 : return s;
123 : }
124 0 : if (len > n)
125 0 : return NULL; /* Buffer too short to skip to the next tag. */
126 0 : s += len; n -= len;
127 0 : }
128 : }
129 :
130 :
131 : /* Locate a TLV encoded data object in BUFFER of LENGTH and
132 : return a pointer to value as well as its length in NBYTES. Return
133 : NULL if it was not found or if the object does not fit into the buffer. */
134 : const unsigned char *
135 0 : find_tlv (const unsigned char *buffer, size_t length,
136 : int tag, size_t *nbytes)
137 : {
138 : const unsigned char *p;
139 :
140 0 : p = do_find_tlv (buffer, length, tag, nbytes, 0);
141 0 : if (p && *nbytes > (length - (p-buffer)))
142 0 : p = NULL; /* Object longer than buffer. */
143 0 : return p;
144 : }
145 :
146 :
147 :
148 : /* Locate a TLV encoded data object in BUFFER of LENGTH and
149 : return a pointer to value as well as its length in NBYTES. Return
150 : NULL if it was not found. Note, that the function does not check
151 : whether the value fits into the provided buffer. */
152 : const unsigned char *
153 0 : find_tlv_unchecked (const unsigned char *buffer, size_t length,
154 : int tag, size_t *nbytes)
155 : {
156 0 : return do_find_tlv (buffer, length, tag, nbytes, 0);
157 : }
158 :
159 :
160 : /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
161 : and the length part from the TLV triplet. Update BUFFER and SIZE
162 : on success. */
163 : gpg_error_t
164 0 : parse_ber_header (unsigned char const **buffer, size_t *size,
165 : int *r_class, int *r_tag,
166 : int *r_constructed, int *r_ndef,
167 : size_t *r_length, size_t *r_nhdr)
168 : {
169 : int c;
170 : unsigned long tag;
171 0 : const unsigned char *buf = *buffer;
172 0 : size_t length = *size;
173 :
174 0 : *r_ndef = 0;
175 0 : *r_length = 0;
176 0 : *r_nhdr = 0;
177 :
178 : /* Get the tag. */
179 0 : if (!length)
180 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
181 0 : c = *buf++; length--; ++*r_nhdr;
182 :
183 0 : *r_class = (c & 0xc0) >> 6;
184 0 : *r_constructed = !!(c & 0x20);
185 0 : tag = c & 0x1f;
186 :
187 0 : if (tag == 0x1f)
188 : {
189 0 : tag = 0;
190 : do
191 : {
192 0 : tag <<= 7;
193 0 : if (!length)
194 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
195 0 : c = *buf++; length--; ++*r_nhdr;
196 0 : tag |= c & 0x7f;
197 :
198 : }
199 0 : while (c & 0x80);
200 : }
201 0 : *r_tag = tag;
202 :
203 : /* Get the length. */
204 0 : if (!length)
205 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
206 0 : c = *buf++; length--; ++*r_nhdr;
207 :
208 0 : if ( !(c & 0x80) )
209 0 : *r_length = c;
210 0 : else if (c == 0x80)
211 0 : *r_ndef = 1;
212 0 : else if (c == 0xff)
213 0 : return gpg_err_make (default_errsource, GPG_ERR_BAD_BER);
214 : else
215 : {
216 0 : unsigned long len = 0;
217 0 : int count = c & 0x7f;
218 :
219 0 : if (count > sizeof (len) || count > sizeof (size_t))
220 0 : return gpg_err_make (default_errsource, GPG_ERR_BAD_BER);
221 :
222 0 : for (; count; count--)
223 : {
224 0 : len <<= 8;
225 0 : if (!length)
226 0 : return gpg_err_make (default_errsource, GPG_ERR_EOF);
227 0 : c = *buf++; length--; ++*r_nhdr;
228 0 : len |= c & 0xff;
229 : }
230 0 : *r_length = len;
231 : }
232 :
233 : /* Without this kludge some example certs can't be parsed. */
234 0 : if (*r_class == CLASS_UNIVERSAL && !*r_tag)
235 0 : *r_length = 0;
236 :
237 0 : *buffer = buf;
238 0 : *size = length;
239 0 : return 0;
240 : }
241 :
242 :
243 : /* FIXME: The following function should not go into this file but for
244 : now it is easier to keep it here. */
245 :
246 : /* Return the next token of an canonical encoded S-expression. BUF
247 : is the pointer to the S-expression and BUFLEN is a pointer to the
248 : length of this S-expression (used to validate the syntax). Both
249 : are updated to reflect the new position. The token itself is
250 : returned as a pointer into the original buffer at TOK and TOKLEN.
251 : If a parentheses is the next token, TOK will be set to NULL.
252 : TOKLEN is checked to be within the bounds. On error an error code
253 : is returned and no pointer is not guaranteed to point to
254 : a meaningful value. DEPTH should be initialized to 0 and will
255 : reflect on return the actual depth of the tree. To detect the end
256 : of the S-expression it is advisable to check DEPTH after a
257 : successful return.
258 :
259 : depth = 0;
260 : while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
261 : && depth)
262 : process_token (tok, toklen);
263 : if (err)
264 : handle_error ();
265 : */
266 : gpg_error_t
267 39 : parse_sexp (unsigned char const **buf, size_t *buflen,
268 : int *depth, unsigned char const **tok, size_t *toklen)
269 : {
270 : const unsigned char *s;
271 : size_t n, vlen;
272 :
273 39 : s = *buf;
274 39 : n = *buflen;
275 39 : *tok = NULL;
276 39 : *toklen = 0;
277 39 : if (!n)
278 0 : return *depth ? gpg_err_make (default_errsource, GPG_ERR_INV_SEXP) : 0;
279 39 : if (*s == '(')
280 : {
281 12 : s++; n--;
282 12 : (*depth)++;
283 12 : *buf = s;
284 12 : *buflen = n;
285 12 : return 0;
286 : }
287 27 : if (*s == ')')
288 : {
289 9 : if (!*depth)
290 0 : return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
291 9 : *toklen = 1;
292 9 : s++; n--;
293 9 : (*depth)--;
294 9 : *buf = s;
295 9 : *buflen = n;
296 9 : return 0;
297 : }
298 42 : for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
299 24 : vlen = vlen*10 + (*s - '0');
300 18 : if (!n || *s != ':')
301 0 : return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
302 18 : s++; n--;
303 18 : if (vlen > n)
304 0 : return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
305 18 : *tok = s;
306 18 : *toklen = vlen;
307 18 : s += vlen;
308 18 : n -= vlen;
309 18 : *buf = s;
310 18 : *buflen = n;
311 18 : return 0;
312 : }
|