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