r16202: Fix Klocwork #3. Strange - was already fixed in HEAD.
[kai/samba.git] / source3 / 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                 SAFE_FREE(nesting);
272                 return False;
273         }
274
275         if (b & 0x80) {
276                 int n = b & 0x7f;
277                 if (!asn1_read_uint8(data, &b)) {
278                         SAFE_FREE(nesting);
279                         return False;
280                 }
281                 nesting->taglen = b;
282                 while (n > 1) {
283                         if (!asn1_read_uint8(data, &b)) {
284                                 SAFE_FREE(nesting);
285                                 return False;
286                         }
287                         nesting->taglen = (nesting->taglen << 8) | b;
288                         n--;
289                 }
290         } else {
291                 nesting->taglen = b;
292         }
293         nesting->start = data->ofs;
294         nesting->next = data->nesting;
295         data->nesting = nesting;
296         return !data->has_error;
297 }
298
299
300 /* stop reading a tag */
301 BOOL asn1_end_tag(ASN1_DATA *data)
302 {
303         struct nesting *nesting;
304
305         /* make sure we read it all */
306         if (asn1_tag_remaining(data) != 0) {
307                 data->has_error = True;
308                 return False;
309         }
310
311         nesting = data->nesting;
312
313         if (!nesting) {
314                 data->has_error = True;
315                 return False;
316         }
317
318         data->nesting = nesting->next;
319         free(nesting);
320         return True;
321 }
322
323 /* work out how many bytes are left in this nested tag */
324 int asn1_tag_remaining(ASN1_DATA *data)
325 {
326         if (data->has_error)
327                 return 0;
328
329         if (!data->nesting) {
330                 data->has_error = True;
331                 return -1;
332         }
333         return data->nesting->taglen - (data->ofs - data->nesting->start);
334 }
335
336 /* read an object ID from a ASN1 buffer */
337 BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
338 {
339         uint8 b;
340         pstring oid_str;
341         fstring el;
342
343         if (!asn1_start_tag(data, ASN1_OID)) return False;
344         asn1_read_uint8(data, &b);
345
346         oid_str[0] = 0;
347         fstr_sprintf(el, "%u",  b/40);
348         pstrcat(oid_str, el);
349         fstr_sprintf(el, " %u",  b%40);
350         pstrcat(oid_str, el);
351
352         while (asn1_tag_remaining(data) > 0) {
353                 unsigned v = 0;
354                 do {
355                         asn1_read_uint8(data, &b);
356                         v = (v<<7) | (b&0x7f);
357                 } while (!data->has_error && b & 0x80);
358                 fstr_sprintf(el, " %u",  v);
359                 pstrcat(oid_str, el);
360         }
361
362         asn1_end_tag(data);
363
364         *OID = SMB_STRDUP(oid_str);
365
366         return !data->has_error;
367 }
368
369 /* check that the next object ID is correct */
370 BOOL asn1_check_OID(ASN1_DATA *data, const char *OID)
371 {
372         char *id;
373
374         if (!asn1_read_OID(data, &id)) return False;
375
376         if (strcmp(id, OID) != 0) {
377                 data->has_error = True;
378                 return False;
379         }
380         free(id);
381         return True;
382 }
383
384 /* read a GeneralString from a ASN1 buffer */
385 BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
386 {
387         int len;
388         if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
389         len = asn1_tag_remaining(data);
390         if (len < 0) {
391                 data->has_error = True;
392                 return False;
393         }
394         *s = SMB_MALLOC(len+1);
395         if (! *s) {
396                 data->has_error = True;
397                 return False;
398         }
399         asn1_read(data, *s, len);
400         (*s)[len] = 0;
401         asn1_end_tag(data);
402         return !data->has_error;
403 }
404
405 /* read a octet string blob */
406 BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
407 {
408         int len;
409         ZERO_STRUCTP(blob);
410         if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
411         len = asn1_tag_remaining(data);
412         if (len < 0) {
413                 data->has_error = True;
414                 return False;
415         }
416         *blob = data_blob(NULL, len);
417         asn1_read(data, blob->data, len);
418         asn1_end_tag(data);
419         return !data->has_error;
420 }
421
422 /* read an interger */
423 BOOL asn1_read_Integer(ASN1_DATA *data, int *i)
424 {
425         uint8 b;
426         *i = 0;
427         
428         if (!asn1_start_tag(data, ASN1_INTEGER)) return False;
429         while (asn1_tag_remaining(data)>0) {
430                 asn1_read_uint8(data, &b);
431                 *i = (*i << 8) + b;
432         }
433         return asn1_end_tag(data);      
434         
435 }
436
437 /* check a enumarted value is correct */
438 BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
439 {
440         uint8 b;
441         if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
442         asn1_read_uint8(data, &b);
443         asn1_end_tag(data);
444
445         if (v != b)
446                 data->has_error = False;
447
448         return !data->has_error;
449 }
450
451 /* write an enumarted value to the stream */
452 BOOL asn1_write_enumerated(ASN1_DATA *data, uint8 v)
453 {
454         if (!asn1_push_tag(data, ASN1_ENUMERATED)) return False;
455         asn1_write_uint8(data, v);
456         asn1_pop_tag(data);
457         return !data->has_error;
458 }