xref: /titanic_52/usr/src/lib/gss_mechs/mech_krb5/krb5/asn.1/asn1buf.c (revision 5caf44512708d13394a092c67a06333261406209)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /* Coding Buffer Implementation */
7 
8 /*
9   Implementation
10 
11     Encoding mode
12 
13     The encoding buffer is filled from bottom (lowest address) to top
14     (highest address).  This makes it easier to expand the buffer,
15     since realloc preserves the existing portion of the buffer.
16 
17     Note: Since ASN.1 encoding must be done in reverse, this means
18     that you can't simply memcpy out the buffer data, since it will be
19     backwards.  You need to reverse-iterate through it, instead.
20 
21     ***This decision may have been a mistake.  In practice, the
22     implementation will probably be tuned such that reallocation is
23     rarely necessary.  Also, the realloc probably has recopy the
24     buffer itself, so we don't really gain that much by avoiding an
25     explicit copy of the buffer.  --Keep this in mind for future reference.
26 
27 
28     Decoding mode
29 
30     The decoding buffer is in normal order and is created by wrapping
31     an asn1buf around a krb5_data structure.
32   */
33 
34 /* Abstraction Function
35 
36    Programs should use just pointers to asn1buf's (e.g. asn1buf *mybuf).
37    These pointers must always point to a valid, allocated asn1buf
38    structure or be NULL.
39 
40    The contents of the asn1buf represent an octet string.  This string
41    begins at base and continues to the octet immediately preceding next.
42    If next == base or mybuf == NULL, then the asn1buf represents an empty
43    octet string. */
44 
45 /* Representation Invariant
46 
47    Pointers to asn1buf's must always point to a valid, allocated
48    asn1buf structure or be NULL.
49 
50    base points to a valid, allocated octet array or is NULL
51    bound, if non-NULL, points to the last valid octet
52    next >= base
53    next <= bound+2  (i.e. next should be able to step just past the bound,
54                      but no further.  (The bound should move out in response
55 		     to being crossed by next.)) */
56 
57 #define ASN1BUF_OMIT_INLINE_FUNCS
58 #include "asn1buf.h"
59 #undef ASN1BUF_OMIT_INLINE_FUNCS
60 #include <stdio.h>
61 #include "asn1_get.h"
62 
63 #define asn1_is_eoc(class, num, indef)	\
64 ((class) == UNIVERSAL && !(num) && !(indef))
65 
66 asn1_error_code asn1buf_create(asn1buf **buf)
67 {
68   *buf = (asn1buf*)malloc(sizeof(asn1buf));
69   if (*buf == NULL) return ENOMEM;
70   (*buf)->base = NULL;
71   (*buf)->bound = NULL;
72   (*buf)->next = NULL;
73   return 0;
74 }
75 
76 asn1_error_code asn1buf_wrap_data(asn1buf *buf, const krb5_data *code)
77 {
78   if(code == NULL || code->data == NULL) return ASN1_MISSING_FIELD;
79   buf->next = buf->base = code->data;
80   buf->bound = code->data + code->length - 1;
81   return 0;
82 }
83 
84 asn1_error_code asn1buf_imbed(asn1buf *subbuf, const asn1buf *buf, const unsigned int length, const int indef)
85 {
86   if (buf->next > buf->bound + 1) return ASN1_OVERRUN;
87   subbuf->base = subbuf->next = buf->next;
88   if (!indef) {
89       if (length > (size_t)(buf->bound + 1 - buf->next)) return ASN1_OVERRUN;
90       subbuf->bound = subbuf->base + length - 1;
91   } else /* constructed indefinite */
92       subbuf->bound = buf->bound;
93   return 0;
94 }
95 
96 asn1_error_code asn1buf_sync(asn1buf *buf, asn1buf *subbuf,
97 			     asn1_class asn1class, asn1_tagnum lasttag,
98 			     unsigned int length, int indef, int seqindef)
99 {
100   asn1_error_code retval;
101 
102   if (!seqindef) {
103     /* sequence was encoded as definite length */
104     buf->next = subbuf->bound + 1;
105   } else if (!asn1_is_eoc(asn1class, lasttag, indef)) {
106       retval = asn1buf_skiptail(subbuf, length, indef);
107       if (retval)
108 	  return retval;
109   } else {
110     /* We have just read the EOC octets. */
111     buf->next = subbuf->next;
112   }
113   return 0;
114 }
115 
116 asn1_error_code asn1buf_skiptail(asn1buf *buf, const unsigned int length, const int indef)
117 {
118   asn1_error_code retval;
119   taginfo t;
120   int nestlevel;
121 
122   nestlevel = 1 + indef;
123   if (!indef) {
124     if (length <= buf->bound - buf->next + 1)
125       buf->next += length;
126     else
127       return ASN1_OVERRUN;
128   }
129   while (nestlevel > 0) {
130     if (buf->bound - buf->next + 1 <= 0)
131       return ASN1_OVERRUN;
132     retval = asn1_get_tag_2(buf, &t);
133     if (retval) return retval;
134     if (!t.indef) {
135       if (t.length <= buf->bound - buf->next + 1)
136 	buf->next += t.length;
137       else
138 	return ASN1_OVERRUN;
139     }
140     if (t.indef)
141       nestlevel++;
142     if (asn1_is_eoc(t.asn1class, t.tagnum, t.indef))
143       nestlevel--;		/* got an EOC encoding */
144   }
145   return 0;
146 }
147 
148 asn1_error_code asn1buf_destroy(asn1buf **buf)
149 {
150   if (*buf != NULL) {
151     if ((*buf)->base != NULL) free((*buf)->base);
152     free(*buf);
153     *buf = NULL;
154   }
155   return 0;
156 }
157 
158 #ifdef asn1buf_insert_octet
159 #undef asn1buf_insert_octet
160 #endif
161 asn1_error_code asn1buf_insert_octet(asn1buf *buf, const int o)
162 {
163   asn1_error_code retval;
164 
165   retval = asn1buf_ensure_space(buf,1U);
166   if(retval) return retval;
167   *(buf->next) = (char)o;
168   (buf->next)++;
169   return 0;
170 }
171 
172 asn1_error_code asn1buf_insert_octetstring(asn1buf *buf, const unsigned int len, const krb5_octet *s)
173 {
174   asn1_error_code retval;
175   int length;
176 
177   retval = asn1buf_ensure_space(buf,len);
178   if(retval) return retval;
179   for(length=1; length<=len; length++,(buf->next)++)
180     *(buf->next) = (char)(s[len-length]);
181   return 0;
182 }
183 
184 asn1_error_code asn1buf_insert_charstring(asn1buf *buf, const unsigned int len, const char *s)
185 {
186   asn1_error_code retval;
187   int length;
188 
189   retval = asn1buf_ensure_space(buf,len);
190   if(retval) return retval;
191   for(length=1; length<=len; length++,(buf->next)++)
192     *(buf->next) = (char)(s[len-length]);
193   return 0;
194 }
195 
196 #undef asn1buf_remove_octet
197 asn1_error_code asn1buf_remove_octet(asn1buf *buf, asn1_octet *o)
198 {
199   if(buf->next > buf->bound) return ASN1_OVERRUN;
200   *o = (asn1_octet)(*((buf->next)++));
201   return 0;
202 }
203 
204 asn1_error_code asn1buf_remove_octetstring(asn1buf *buf, const unsigned int len, asn1_octet **s)
205 {
206   int i;
207 
208   if (buf->next > buf->bound + 1) return ASN1_OVERRUN;
209   if (len > buf->bound + 1 - buf->next) return ASN1_OVERRUN;
210   if (len == 0) {
211       *s = 0;
212       return 0;
213   }
214   *s = (asn1_octet*)malloc(len*sizeof(asn1_octet));
215   if (*s == NULL)
216       return ENOMEM;
217   for(i=0; i<len; i++)
218     (*s)[i] = (asn1_octet)(buf->next)[i];
219   buf->next += len;
220   return 0;
221 }
222 
223 asn1_error_code asn1buf_remove_charstring(asn1buf *buf, const unsigned int len, char **s)
224 {
225   int i;
226 
227   if (buf->next > buf->bound + 1) return ASN1_OVERRUN;
228   if (len > buf->bound + 1 - buf->next) return ASN1_OVERRUN;
229   if (len == 0) {
230       *s = 0;
231       return 0;
232   }
233   *s = (char*)malloc(len*sizeof(char));
234   if (*s == NULL) return ENOMEM;
235   for(i=0; i<len; i++)
236     (*s)[i] = (char)(buf->next)[i];
237   buf->next += len;
238   return 0;
239 }
240 
241 int asn1buf_remains(asn1buf *buf, int indef)
242 {
243   int remain;
244   if(buf == NULL || buf->base == NULL) return 0;
245   remain = buf->bound - buf->next +1;
246   if (remain <= 0) return remain;
247   /*
248    * Two 0 octets means the end of an indefinite encoding.
249    */
250   if (indef && remain >= 2 && !*(buf->next) && !*(buf->next + 1))
251       return 0;
252   else return remain;
253 }
254 
255 asn1_error_code asn12krb5_buf(const asn1buf *buf, krb5_data **code)
256 {
257   int i;
258   *code = (krb5_data*)calloc(1,sizeof(krb5_data));
259   if(*code == NULL) return ENOMEM;
260   (*code)->magic = KV5M_DATA;
261   (*code)->data = NULL;
262   (*code)->length = 0;
263   (*code)->length = asn1buf_len(buf);
264   (*code)->data = (char*)malloc((((*code)->length)+1)*sizeof(char));
265   if ((*code)->data == NULL) {
266     free(*code);
267     *code = NULL;
268     return ENOMEM;
269   }
270   for(i=0; i < (*code)->length; i++)
271     ((*code)->data)[i] = (buf->base)[((*code)->length)-i-1];
272   ((*code)->data)[(*code)->length] = '\0';
273   return 0;
274 }
275 
276 
277 
278 /* These parse and unparse procedures should be moved out. They're
279    useful only for debugging and superfluous in the production version. */
280 
281 asn1_error_code asn1buf_unparse(const asn1buf *buf, char **s)
282 {
283   if(*s != NULL) free(*s);
284   if(buf == NULL){
285     *s = malloc(sizeof("<NULL>"));
286     if(*s == NULL) return ENOMEM;
287     strcpy(*s,"<NULL>");
288   }else if(buf->base == NULL){
289     *s = malloc(sizeof("<EMPTY>"));
290     if(*s == NULL) return ENOMEM;
291     strcpy(*s,"<EMPTY>");
292   }else{
293     unsigned int length = asn1buf_len(buf);
294     int i;
295 
296     *s = calloc(length+1, sizeof(char));
297     if(*s == NULL) return ENOMEM;
298     (*s)[length] = '\0';
299     for(i=0; i<length; i++) ;
300 /*      OLDDECLARG( (*s)[i] = , (buf->base)[length-i-1]) */
301   }
302   return 0;
303 }
304 
305 asn1_error_code asn1buf_hex_unparse(const asn1buf *buf, char **s)
306 {
307 #define hexchar(d) ((d)<=9 ? ('0'+(d)) :\
308 		    ((d)<=15 ? ('A'+(d)-10) :\
309 		    'X'))
310 
311   if(*s != NULL) free(*s);
312 
313   if(buf == NULL){
314     *s = malloc(sizeof("<NULL>"));
315     if(*s == NULL) return ENOMEM;
316     strcpy(*s,"<NULL>");
317   }else if(buf->base == NULL){
318     *s = malloc(sizeof("<EMPTY>"));
319     if(*s == NULL) return ENOMEM;
320     strcpy(*s,"<EMPTY>");
321   }else{
322     unsigned int length = asn1buf_len(buf);
323     int i;
324 
325     *s = malloc(3*length);
326     if(*s == NULL) return ENOMEM;
327     for(i = length-1; i >= 0; i--){
328       (*s)[3*(length-i-1)] = hexchar(((buf->base)[i]&0xF0)>>4);
329       (*s)[3*(length-i-1)+1] = hexchar((buf->base)[i]&0x0F);
330       (*s)[3*(length-i-1)+2] = ' ';
331     }
332     (*s)[3*length-1] = '\0';
333   }
334   return 0;
335 }
336 
337 /****************************************************************/
338 /* Private Procedures */
339 
340 #undef asn1buf_size
341 int asn1buf_size(const asn1buf *buf)
342 {
343   if(buf == NULL || buf->base == NULL) return 0;
344   return buf->bound - buf->base + 1;
345 }
346 
347 #undef asn1buf_free
348 int asn1buf_free(const asn1buf *buf)
349 {
350   if(buf == NULL || buf->base == NULL) return 0;
351   else return buf->bound - buf->next + 1;
352 }
353 
354 #undef asn1buf_ensure_space
355 asn1_error_code asn1buf_ensure_space(asn1buf *buf, const unsigned int amount)
356 {
357   int avail = asn1buf_free(buf);
358   if(avail < amount){
359     asn1_error_code retval = asn1buf_expand(buf, amount-avail);
360     if(retval) return retval;
361   }
362   return 0;
363 }
364 
365 asn1_error_code asn1buf_expand(asn1buf *buf, unsigned int inc)
366 {
367 #define STANDARD_INCREMENT 200
368   int next_offset = buf->next - buf->base;
369   int bound_offset;
370   if (buf->base == NULL) bound_offset = -1;
371   else bound_offset = buf->bound - buf->base;
372 
373   if (inc < STANDARD_INCREMENT)
374     inc = STANDARD_INCREMENT;
375 
376   if (buf->base == NULL)
377     buf->base = malloc((asn1buf_size(buf)+inc) * sizeof(asn1_octet));
378   else
379     buf->base = realloc(buf->base,
380 			(asn1buf_size(buf)+inc) * sizeof(asn1_octet));
381   if (buf->base == NULL) return ENOMEM;
382   buf->bound = (buf->base) + bound_offset + inc;
383   buf->next = (buf->base) + next_offset;
384   return 0;
385 }
386 
387 #undef asn1buf_len
388 int asn1buf_len(const asn1buf *buf)
389 {
390   return buf->next - buf->base;
391 }
392