1 /*
2    This file is part of GNU Radius SNMP Library.
3    Copyright (C) 2001,2003,2004,2007 Free Software Foundation, Inc.
4 
5    Written by Sergey Poznyakoff
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 3 of the
10    License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21 
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25 #include <snmp/asn1.h>
26 #include <snmp/snmp.h>
27 
28 #ifndef SNMP_SET_ERRNO
29 # define SNMP_SET_ERRNO(e)
30 #endif
31 
32 /* Obtain the length of a current object.
33    Input:  data points to the start of object length
34    Output: length contains object length.
35    Return: pointer to the first byte of the object.
36    Error:  Returns NULL, does not change args */
37 u_char *
asn_decode_length(u_char * data,u_int * length)38 asn_decode_length(u_char *data, u_int *length)
39 {
40         u_char size = *data++;
41 
42         if (size & ASN_LONG_LEN) {
43                 u_int len;
44 
45                 size &= ~ASN_LONG_LEN;  /* size contains the actual count
46                                            of length bytes that follow */
47                 if (!size || size > sizeof(int)) {
48                         SNMP_SET_ERRNO(E_ASN_DECODE);
49                         return NULL;
50                 }
51 
52                 len = 0;
53                 while (size--) {
54                         len <<= 8;
55                         len |= *data++;
56                 }
57                 *length = len;
58         } else
59                 *length = size;
60         return data;
61 }
62 
63 /* Encode object length.
64    Input: data points to data space
65           *datalength contains size of data space
66    Output:*datalength contains number of bytes left in the data space
67    Return:Pointer to the byte immediately past the encoded length
68    Error: NULL */
69 u_char *
asn_encode_length(u_char * data,int * datalength,u_int length)70 asn_encode_length(u_char *data, int *datalength, u_int length)
71 {
72         if (*datalength < 1) {
73                 SNMP_SET_ERRNO(E_ASN_ENCODE);
74                 return NULL;
75         }
76 
77         if (length < 0x80) {
78                 *data++ = (u_char) length;
79         } else if (length < 0xff) {
80                 if (*datalength < 2) {
81                         SNMP_SET_ERRNO(E_ASN_ENCODE);
82                         return NULL;
83                 }
84 
85                 *data++ = (u_char)(ASN_LONG_LEN|0x01);
86                 *data++ = (u_char)length;
87         } else {
88                 /*length can be at most 0xffff */
89                 if (*datalength < 3) {
90                         SNMP_SET_ERRNO(E_ASN_ENCODE);
91                         return NULL;
92                 }
93 
94                 *data++ = (u_char)(ASN_LONG_LEN|0x02);
95                 *data++ = (u_char)((length >> 8) & 0xff);
96                 *data++ = (u_char)(length & 0xff);
97         }
98         return data;
99 }
100 
101 u_char *
asn_recode_length(u_char * data,u_int length)102 asn_recode_length(u_char *data, u_int length)
103 {
104         /*length can be at most 0xffff */
105         *data++ = (u_char)(ASN_LONG_LEN|0x02);
106         *data++ = (u_char)((length >> 8) & 0xff);
107         *data++ = (u_char)(length & 0xff);
108         return data;
109 }
110 
111 /* Build an ASN header.
112    Input: data points to data space;
113           *datalength contains size of data space;
114           type is the object type;
115           length is object length.
116    Output:*datalength contains number of bytes left in the data space
117    Return:Pointer to the byte immediately past the header (i.e. start of
118           data).
119    Error: NULL */
120 u_char *
asn_encode_header(u_char * data,int * datalength,u_char type,int length)121 asn_encode_header(u_char *data, int *datalength, u_char type, int length)
122 {
123         if (*datalength < 1) {
124                 SNMP_SET_ERRNO(E_ASN_ENCODE);
125                 return (NULL);
126         }
127         *data++ = type;
128         --*datalength;
129         return asn_encode_length(data, datalength, length);
130 }
131 
132 u_char *
asn_decode_header(u_char * data,int * datalength,u_char * type)133 asn_decode_header(u_char *data, int *datalength, u_char *type)
134 {
135         if (*datalength < 1) {
136                 SNMP_SET_ERRNO(E_ASN_DECODE);
137                 return (NULL);
138         }
139         *type = *data++;
140         --*datalength;
141         return asn_decode_length(data, datalength);
142 }
143 
144 u_char *
asn_encode_null(u_char * data,int * datalength,u_char type)145 asn_encode_null(u_char *data, int *datalength, u_char type)
146 {
147         return asn_encode_header(data, datalength, type, 0);
148 }
149 
150 /* Integer objects
151    ASN.1 spec:
152       integer ::= 0x02 asnlength byte {byte}*   */
153 
154 /* Decode an integer object.
155    Input: data => dataspace;
156           datalength => size of dataspace;
157           intp - output buffer for integer value;
158           intsize => size of output buffer.
159    Output:datalength => number of bytes left in dataspace;
160           type => asn type of object (alvays 0x02);
161           intp => decoded integer value.
162    Return:Pointer to the byte immediately past the decoded object.
163    Error: NULL */
164 u_char *
asn_decode_int(u_char * data,int * datalength,u_char * type,int * intp,int intsize)165 asn_decode_int(u_char *data, int *datalength, u_char *type,
166 	       int *intp, int intsize)
167 {
168         u_int count;
169         u_char *buf = data;
170         int value;
171 
172         if (intsize != sizeof(int)) {
173                 SNMP_SET_ERRNO(E_ASN_DECODE);
174                 return NULL;
175         }
176         *type = *buf++;
177         buf = asn_decode_length(buf, &count);
178         if (!buf)
179                 return buf;
180 
181         if (count + (buf - data) > *datalength) /* || count > intsize)*/ {
182                 SNMP_SET_ERRNO(E_ASN_DECODE);
183                 return NULL;
184         }
185 
186         /* Initialize value */
187         if (*buf & 0x80)
188                 value = -1;
189         else
190                 value = 0;
191 
192         while (count--) {
193                 value <<= 8;
194                 value |= *buf++;
195         }
196 
197         *datalength -= buf - data;
198         *intp = value;
199         return buf;
200 }
201 
202 /* build an ASN object containig an integer
203    Input: data => dataspace;
204           datalength => size of dataspace;
205           type - object type;
206           intval integer value to be encoded;
207    Output:datalength => number of bytes left in dataspace.
208    Return:Pointer to the byte immediately past the encoded object.
209    Error: NULL */
210 
211 u_char *
asn_encode_int(u_char * data,int * datalength,u_char type,int intval)212 asn_encode_int(u_char *data, int *datalength, u_char type, int intval)
213 {
214         u_int mask;
215         u_int intsize = sizeof(int);
216 
217         /*
218          * Truncate "unnecessary" bytes off of the most significant end of this
219          * 2's complement integer.  There should be no sequence of 9
220          * consecutive 1's or 0's at the most significant end of the
221          * integer.
222          */
223         mask = (u_int) 0x1FF << ((8 * (sizeof(int) - 1)) - 1);
224 
225         while ((((intval & mask) == 0) || ((intval & mask) == mask))
226                && intsize > 1) {
227                 intsize--;
228                 intval <<= 8;
229         }
230 
231         if (!(data = asn_encode_header(data, datalength, type, intsize)) ||
232             *datalength < intsize) {
233                 SNMP_SET_ERRNO(E_ASN_ENCODE);
234                 return NULL;
235         }
236 
237         *datalength -= intsize;
238         mask = (u_int) 0xff << (8 * (sizeof(int) - 1));
239         while (intsize--) {
240                 *data++ = (u_char) ((intval & mask) >> (8*(sizeof(int) - 1)));
241                 intval <<= 8;
242         }
243 
244         return data;
245 }
246 
247 /* String objects.
248    ASN.1 spec:
249        octet string ::= primstring | cmpdstring
250        primstring ::= 0x04 asnlength byte {byte}*
251        cmpdstring ::= 0x24 asnlength string {string}* */
252 
253 /* Decode a string object.
254    Input: data => dataspace;
255           datalength => size of dataspace;
256           string - output buffer for octet string;
257           strlength => size of output buffer.
258    Output:datalength => number of bytes left in dataspace;
259           type => asn type of object;
260           string => decoded octet string;
261           strlength => number of bytes actually used in string.
262    Return:Pointer to the byte immediately past the decoded object.
263    Error: NULL */
264 u_char *
asn_decode_string(u_char * data,int * datalength,u_char * type,u_char * string,int * strlength)265 asn_decode_string(u_char *data, int *datalength, u_char *type,
266 		  u_char *string, int *strlength)
267 {
268         u_char *buf = data;
269         u_int count;
270 
271         *type = *buf++;
272         if ((buf = asn_decode_length(buf, &count)) == NULL ||
273             count + (buf - data) > *datalength ||
274             count > *strlength) {
275                 SNMP_SET_ERRNO(E_ASN_DECODE);
276                 return NULL;
277         }
278 
279         *strlength = count;
280         while (count--)
281                 *string++ = *buf++;
282         *datalength -= buf - data;
283         return buf;
284 }
285 
286 /* build an ASN object containig a string.
287    Input: data => dataspace;
288           datalength => size of dataspace;
289           type - object type;
290           string - conents of the string object;
291           strlength - number of bytes in string
292    Output:datalength => number of bytes left in dataspace.
293    Return:Pointer to the byte immediately past the encoded object.
294    Error: NULL */
295 u_char *
asn_encode_string(u_char * data,int * datalength,u_char type,u_char * string,int strlength)296 asn_encode_string(u_char *data, int *datalength, u_char type,
297 		  u_char *string, int strlength)
298 {
299         data = asn_encode_header(data, datalength, type, strlength);
300         if (!data || *datalength < strlength) {
301                 SNMP_SET_ERRNO(E_ASN_ENCODE);
302                 return NULL;
303         }
304         *datalength -= strlength;
305         while (strlength--)
306                 *data++ = *string++;
307         return data;
308 }
309 
310 /* Object Identifiers
311    ASN.1 spec
312       objid ::= 0x06 asnlength subidentifier {subidentifier}*
313       subidentifier ::= {leadingbyte}* lastbyte
314       leadingbyte ::= 1 7bitvalue
315       lastbyte ::= 0 7bitvalue */
316 
317 #define BOGUS_40
318 
319 /* Decode an oid object.
320    Input: data => dataspace;
321           datalength => size of dataspace;
322           type => object type;
323           obid => oid output buffer.
324           obidlength => size of output buffer.
325    Output:datalength => number of bytes left in dataspace;
326           type => asn type of object;
327           obid => decoded oid;
328           obidlength => real oid length.
329    Return:Pointer to the byte immediately past the decoded object.
330    Error: NULL */
331 u_char *
asn_decode_oid(u_char * data,int * datalength,u_char * type,oid_t obid,int * obidlength)332 asn_decode_oid(u_char *data, int *datalength, u_char *type,
333 	       oid_t obid, int *obidlength)
334 {
335         u_char *buf = data;
336         u_int count;
337         u_int subid;
338         oid_t oidp = obid + 1;
339 
340         *type = *buf++;
341         buf = asn_decode_length(buf, &count);
342         if (!buf || count + (buf - data) > *datalength) {
343                 SNMP_SET_ERRNO(E_ASN_DECODE);
344                 return NULL;
345         }
346 
347         if (count == 0)
348                 obid[0] = obid[1] = 0;
349 
350         --*obidlength;
351         while (count > 0 && (*obidlength)-- > 0) {
352 
353                 /* {leadingbyte}* */
354                 subid = 0;
355                 do {
356                         subid = (subid<<7)+(*buf & ~ASN_BIT8);
357                         count--;
358                 } while (*buf++ & ASN_BIT8);
359 
360                 if (subid > MAX_SUBID) {
361                         SNMP_SET_ERRNO(E_ASN_DECODE);
362                         return NULL;
363                 }
364                 *oidp++ = (subid_t) subid;
365         }
366 #ifdef BOGUS_40
367         /* The first subid is actually oid[0]*40+oid[1].
368            I don't know why. */
369         subid = (u_int)obid[1];
370         obid[1] = (u_char) (subid % 40);
371         obid[0] = (u_char) ((subid-obid[1])/40);
372 #endif
373         *obidlength = (int) (oidp - obid);
374         *datalength -= (buf-data);
375         return buf;
376 }
377 
378 /* build an ASN object containig an oid.
379    Input: data => dataspace;
380           datalength => size of dataspace;
381           type - object type;
382           obid - oid value;
383           obidlength - length of obid.
384    Output:datalength => number of bytes left in dataspace.
385    Return:Pointer to the byte immediately past the encoded object.
386    Error: NULL */
387 u_char *
asn_encode_oid(u_char * data,int * datalength,u_char type,oid_t obid,int obidlength)388 asn_encode_oid(u_char *data, int *datalength, u_char type,
389 	       oid_t obid, int obidlength)
390 {
391         int length;
392         u_char *buf, *bp;
393         u_int subid;
394 
395 #define CHKLEN() if (length-- < 1) goto err;
396 
397         length = *datalength;
398         buf = asn_encode_header(data, &length, type, 0xffff);
399         if (!buf) {
400 err:
401                 SNMP_SET_ERRNO(E_ASN_ENCODE);
402                 return NULL;
403         }
404 
405         bp = buf;
406 #ifdef BOGUS_40
407         if (obidlength < 2) {
408                 *bp++ = 0;
409                 obidlength = 0;
410         } else {
411                 *bp++ = obid[1] + (obid[0] * 40);
412                 obidlength -= 2;
413                 obid += 2;
414         }
415 #endif
416         while (obidlength--) {
417                 subid = *obid++;
418                 if (subid < 0x7f) { /* lastbyte */
419                         CHKLEN();
420                         *bp++ = subid;
421                 } else { /* leadingbyte */
422                         register u_char *p, *q, tb;
423 
424                         p = bp;
425                         for (; subid > 0x7f; subid >>= 7) {
426                                 CHKLEN();
427                                 *bp++ = (u_char)((subid & 0x7f)|ASN_BIT8);
428                         }
429 
430                         /* add lastbyte */
431                         CHKLEN();
432                         *bp++ = (u_char) subid | ASN_BIT8;
433 
434                         /* swap bytes */
435                         for (q = bp-1; q > p; q--, p++) {
436                                 tb = *q;
437                                 *q = *p;
438                                 *p = tb;
439                         }
440                         /* make lastbyte 7-bit */
441                         bp[-1] &= ~ASN_BIT8;
442                 }
443         }
444 
445         asn_recode_length(data+1, bp - buf);
446         *datalength = length;
447         return bp;
448 }
449 
450