r13915: Fixed a very interesting class of realloc() bugs found by Coverity.
[samba.git] / source / libsmb / asn1.c
1 /* 
2    Unix SMB/CIFS implementation.
3    simple SPNEGO routines
4    Copyright (C) Andrew Tridgell 2001
5    
6    This program 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    This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 /* free an asn1 structure */
24 void asn1_free(ASN1_DATA *data)
25 {
26         SAFE_FREE(data->data);
27 }
28
29 /* write to the ASN1 buffer, advancing the buffer pointer */
30 BOOL asn1_write(ASN1_DATA *data, const void *p, int len)
31 {
32         if (data->has_error) return False;
33         if (data->length < data->ofs+len) {
34                 data->data = SMB_REALLOC(data->data, data->ofs+len);
35                 if (!data->data) {
36                         data->has_error = True;
37                         return False;
38                 }
39                 data->length = data->ofs+len;
40         }
41         memcpy(data->data + data->ofs, p, len);
42         data->ofs += len;
43         return True;
44 }
45
46 /* useful fn for writing a uint8 */
47 BOOL asn1_write_uint8(ASN1_DATA *data, uint8 v)
48 {
49         return asn1_write(data, &v, 1);
50 }
51
52 /* push a tag onto the asn1 data buffer. Used for nested structures */
53 BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
54 {
55         struct nesting *nesting;
56
57         asn1_write_uint8(data, tag);
58         nesting = SMB_MALLOC_P(struct nesting);
59         if (!nesting) {
60                 data->has_error = True;
61                 return False;
62         }
63
64         nesting->start = data->ofs;
65         nesting->next = data->nesting;
66         data->nesting = nesting;
67         return asn1_write_uint8(data, 0xff);
68 }
69
70 /* pop a tag */
71 BOOL asn1_pop_tag(ASN1_DATA *data)
72 {
73         struct nesting *nesting;
74         size_t len;
75
76         nesting = data->nesting;
77
78         if (!nesting) {
79                 data->has_error = True;
80                 return False;
81         }
82         len = data->ofs - (nesting->start+1);
83         /* yes, this is ugly. We don't know in advance how many bytes the length
84            of a tag will take, so we assumed 1 byte. If we were wrong then we 
85            need to correct our mistake */
86         if (len > 0xFFFF) {
87                 data->data[nesting->start] = 0x83;
88                 if (!asn1_write_uint8(data, 0)) return False;
89                 if (!asn1_write_uint8(data, 0)) return False;
90                 if (!asn1_write_uint8(data, 0)) return False;
91                 memmove(data->data+nesting->start+4, data->data+nesting->start+1, len);
92                 data->data[nesting->start+1] = (len>>16) & 0xFF;
93                 data->data[nesting->start+2] = (len>>8) & 0xFF;
94                 data->data[nesting->start+3] = len&0xff;
95         } else if (len > 255) {
96                 data->data[nesting->start] = 0x82;
97                 if (!asn1_write_uint8(data, 0)) return False;
98                 if (!asn1_write_uint8(data, 0)) return False;
99                 memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
100                 data->data[nesting->start+1] = len>>8;
101                 data->data[nesting->start+2] = len&0xff;
102         } else if (len > 127) {
103                 data->data[nesting->start] = 0x81;
104                 if (!asn1_write_uint8(data, 0)) return False;
105                 memmove(data->data+nesting->start+2, data->data+nesting->start+1, len);
106                 data->data[nesting->start+1] = len;
107         } else {
108                 data->data[nesting->start] = len;
109         }
110
111         data->nesting = nesting->next;
112         free(nesting);
113         return True;
114 }
115
116
117 /* write an integer */
118 BOOL asn1_write_Integer(ASN1_DATA *data, int i)
119 {
120         if (!asn1_push_tag(data, ASN1_INTEGER)) return False;
121         do {
122                 asn1_write_uint8(data, i);
123                 i = i >> 8;
124         } while (i);
125         return asn1_pop_tag(data);
126 }
127
128 /* write an object ID to a ASN1 buffer */
129 BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
130 {
131         unsigned v, v2;
132         const char *p = (const char *)OID;
133         char *newp;
134
135         if (!asn1_push_tag(data, ASN1_OID))
136                 return False;
137         v = strtol(p, &newp, 10);
138         p = newp;
139         v2 = strtol(p, &newp, 10);
140         p = newp;
141         if (!asn1_write_uint8(data, 40*v + v2))
142                 return False;
143
144         while (*p) {
145                 v = strtol(p, &newp, 10);
146                 p = newp;
147                 if (v >= (1<<28)) asn1_write_uint8(data, 0x80 | ((v>>28)&0xff));
148                 if (v >= (1<<21)) asn1_write_uint8(data, 0x80 | ((v>>21)&0xff));
149                 if (v >= (1<<14)) asn1_write_uint8(data, 0x80 | ((v>>14)&0xff));
150                 if (v >= (1<<7)) asn1_write_uint8(data, 0x80 | ((v>>7)&0xff));
151                 if (!asn1_write_uint8(data, v&0x7f))
152                         return False;
153         }
154         return asn1_pop_tag(data);
155 }
156
157 /* write an octet string */
158 BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length)
159 {
160         asn1_push_tag(data, ASN1_OCTET_STRING);
161         asn1_write(data, p, length);
162         asn1_pop_tag(data);
163         return !data->has_error;
164 }
165
166 /* write a general string */
167 BOOL asn1_write_GeneralString(ASN1_DATA *data, const char *s)
168 {
169         asn1_push_tag(data, ASN1_GENERAL_STRING);
170         asn1_write(data, s, strlen(s));
171         asn1_pop_tag(data);
172         return !data->has_error;
173 }
174
175 /* write a BOOLEAN */
176 BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v)
177 {
178         asn1_write_uint8(data, ASN1_BOOLEAN);
179         asn1_write_uint8(data, v);
180         return !data->has_error;
181 }
182
183 /* write a BOOLEAN - hmm, I suspect this one is the correct one, and the 
184    above boolean is bogus. Need to check */
185 BOOL asn1_write_BOOLEAN2(ASN1_DATA *data, BOOL v)
186 {
187         asn1_push_tag(data, ASN1_BOOLEAN);
188         asn1_write_uint8(data, v);
189         asn1_pop_tag(data);
190         return !data->has_error;
191 }
192
193 /* check a BOOLEAN */
194 BOOL asn1_check_BOOLEAN(ASN1_DATA *data, BOOL v)
195 {
196         uint8 b = 0;
197
198         asn1_read_uint8(data, &b);
199         if (b != ASN1_BOOLEAN) {
200                 data->has_error = True;
201                 return False;
202         }
203         asn1_read_uint8(data, &b);
204         if (b != v) {
205                 data->has_error = True;
206                 return False;
207         }
208         return !data->has_error;
209 }
210
211
212 /* load a ASN1_DATA structure with a lump of data, ready to be parsed */
213 BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
214 {
215         ZERO_STRUCTP(data);
216         data->data = memdup(blob.data, blob.length);
217         if (!data->data) {
218                 data->has_error = True;
219                 return False;
220         }
221         data->length = blob.length;
222         return True;
223 }
224
225 /* read from a ASN1 buffer, advancing the buffer pointer */
226 BOOL asn1_read(ASN1_DATA *data, void *p, int len)
227 {
228         if (data->has_error)
229                 return False;
230
231         if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len) {
232                 data->has_error = True;
233                 return False;
234         }
235
236         if (data->ofs + len > data->length) {
237                 data->has_error = True;
238                 return False;
239         }
240         memcpy(p, data->data + data->ofs, len);
241         data->ofs += len;
242         return True;
243 }
244
245 /* read a uint8 from a ASN1 buffer */
246 BOOL asn1_read_uint8(ASN1_DATA *data, uint8 *v)
247 {
248         return asn1_read(data, v, 1);
249 }
250
251 /* start reading a nested asn1 structure */
252 BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag)
253 {
254         uint8 b;
255         struct nesting *nesting;
256         
257         if (!asn1_read_uint8(data, &b))
258                 return False;
259
260         if (b != tag) {
261                 data->has_error = True;
262                 return False;
263         }
264         nesting = SMB_MALLOC_P(struct nesting);
265         if (!nesting) {
266                 data->has_error = True;
267                 return False;
268         }
269
270         if (!asn1_read_uint8(data, &b)) {
271                 return False;
272         }
273
274         if (b & 0x80) {
275                 int n = b & 0x7f;
276                 if (!asn1_read_uint8(data, &b))
277                         return False;
278                 nesting->taglen = b;
279                 while (n > 1) {
280                         if (!asn1_read_uint8(data, &b)) 
281                                 return False;
282                         nesting->taglen = (nesting->taglen << 8) | b;
283                         n--;
284                 }
285         } else {
286                 nesting->taglen = b;
287         }
288         nesting->start = data->ofs;
289         nesting->next = data->nesting;
290         data->nesting = nesting;
291         return !data->has_error;
292 }
293
294
295 /* stop reading a tag */
296 BOOL asn1_end_tag(ASN1_DATA *data)
297 {
298         struct nesting *nesting;
299
300         /* make sure we read it all */
301         if (asn1_tag_remaining(data) != 0) {
302                 data->has_error = True;
303                 return False;
304         }
305
306         nesting = data->nesting;
307
308         if (!nesting) {
309                 data->has_error = True;
310                 return False;
311         }
312
313         data->nesting = nesting->next;
314         free(nesting);
315         return True;
316 }
317
318 /* work out how many bytes are left in this nested tag */
319 int asn1_tag_remaining(ASN1_DATA *data)
320 {
321         if (data->has_error)
322                 return 0;
323
324         if (!data->nesting) {
325                 data->has_error = True;
326                 return -1;
327         }
328         return data->nesting->taglen - (data->ofs - data->nesting->start);
329 }
330
331 /* read an object ID from a ASN1 buffer */
332 BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
333 {
334         uint8 b;
335         pstring oid_str;
336         fstring el;
337
338         if (!asn1_start_tag(data, ASN1_OID)) return False;
339         asn1_read_uint8(data, &b);
340
341         oid_str[0] = 0;
342         fstr_sprintf(el, "%u",  b/40);
343         pstrcat(oid_str, el);
344         fstr_sprintf(el, " %u",  b%40);
345         pstrcat(oid_str, el);
346
347         while (asn1_tag_remaining(data) > 0) {
348                 unsigned v = 0;
349                 do {
350                         asn1_read_uint8(data, &b);
351                         v = (v<<7) | (b&0x7f);
352                 } while (!data->has_error && b & 0x80);
353                 fstr_sprintf(el, " %u",  v);
354                 pstrcat(oid_str, el);
355         }
356
357         asn1_end_tag(data);
358
359         *OID = SMB_STRDUP(oid_str);
360
361         return !data->has_error;
362 }
363
364 /* check that the next object ID is correct */
365 BOOL asn1_check_OID(ASN1_DATA *data, const char *OID)
366 {
367         char *id;
368
369         if (!asn1_read_OID(data, &id)) return False;
370
371         if (strcmp(id, OID) != 0) {
372                 data->has_error = True;
373                 return False;
374         }
375         free(id);
376         return True;
377 }
378
379 /* read a GeneralString from a ASN1 buffer */
380 BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
381 {
382         int len;
383         if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
384         len = asn1_tag_remaining(data);
385         if (len < 0) {
386                 data->has_error = True;
387                 return False;
388         }
389         *s = SMB_MALLOC(len+1);
390         if (! *s) {
391                 data->has_error = True;
392                 return False;
393         }
394         asn1_read(data, *s, len);
395         (*s)[len] = 0;
396         asn1_end_tag(data);
397         return !data->has_error;
398 }
399
400 /* read a octet string blob */
401 BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
402 {
403         int len;
404         ZERO_STRUCTP(blob);
405         if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
406         len = asn1_tag_remaining(data);
407         if (len < 0) {
408                 data->has_error = True;
409                 return False;
410         }
411         *blob = data_blob(NULL, len);
412         asn1_read(data, blob->data, len);
413         asn1_end_tag(data);
414         return !data->has_error;
415 }
416
417 /* read an interger */
418 BOOL asn1_read_Integer(ASN1_DATA *data, int *i)
419 {
420         uint8 b;
421         *i = 0;
422         
423         if (!asn1_start_tag(data, ASN1_INTEGER)) return False;
424         while (asn1_tag_remaining(data)>0) {
425                 asn1_read_uint8(data, &b);
426                 *i = (*i << 8) + b;
427         }
428         return asn1_end_tag(data);      
429         
430 }
431
432 /* check a enumarted value is correct */
433 BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
434 {
435         uint8 b;
436         if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
437         asn1_read_uint8(data, &b);
438         asn1_end_tag(data);
439
440         if (v != b)
441                 data->has_error = False;
442
443         return !data->has_error;
444 }
445
446 /* write an enumarted value to the stream */
447 BOOL asn1_write_enumerated(ASN1_DATA *data, uint8 v)
448 {
449         if (!asn1_push_tag(data, ASN1_ENUMERATED)) return False;
450         asn1_write_uint8(data, v);
451         asn1_pop_tag(data);
452         return !data->has_error;
453 }