~ubuntu-branches/ubuntu/oneiric/gnupg2/oneiric-updates

« back to all changes in this revision

Viewing changes to scd/tlv.c

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Viehmann
  • Date: 2008-10-04 10:25:53 UTC
  • mfrom: (5.1.15 intrepid)
  • Revision ID: james.westby@ubuntu.com-20081004102553-fv62pp8dsitxli47
Tags: 2.0.9-3.1
* Non-maintainer upload.
* agent/gpg-agent.c: Deinit the threading library before exec'ing
  the command to run in --daemon mode. And because that still doesn't
  restore the sigprocmask, do that manually. Closes: #499569

Show diffs side-by-side

added added

removed removed

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