4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * ASN.1 encoding related routines
36 #include <sys/types.h>
42 * This routine builds a 'SEQUENCE OF' ASN.1 object in the buffer
43 * using the 'id' and 'length' supplied. This is probably the place
44 * where using "reverse" asn encoding will help.
47 asn_build_sequence(uchar_t
*buf
, size_t *bufsz_p
, uchar_t id
, size_t length
)
50 * When rebuilding sequence (which we do many times), we'll
51 * simply pass NULL to bufsz_p to skip the error check.
53 if ((bufsz_p
) && (*bufsz_p
< 4))
57 buf
[1] = (uchar_t
)(ASN_LONG_LEN
| 0x02); /* following 2 octets */
58 buf
[2] = (uchar_t
)((length
>> 8) & 0xff);
59 buf
[3] = (uchar_t
)(length
& 0xff);
70 * The next two routines, asn_build_header() and asn_build_length(), build
71 * the header and length for an arbitrary object type into the buffer. The
72 * length of the object is encoded using as few length octets as possible.
75 asn_build_header(uchar_t
*buf
, size_t *bufsz_p
, uchar_t id
, size_t length
)
83 return (asn_build_length(buf
+ 1, bufsz_p
, length
));
86 asn_build_length(uchar_t
*buf
, size_t *bufsz_p
, size_t length
)
91 buf
[0] = (uchar_t
)length
;
98 } else if (length
<= 0xFF) {
101 buf
[0] = (uchar_t
)(ASN_LONG_LEN
| 0x01);
102 buf
[1] = (uchar_t
)length
;
105 LOGASNLENGTH(buf
, 2);
113 buf
[0] = (uchar_t
)(ASN_LONG_LEN
| 0x02);
114 buf
[1] = (uchar_t
)((length
>> 8) & 0xff);
115 buf
[2] = (uchar_t
)(length
& 0xff);
118 LOGASNLENGTH(buf
, 3);
124 * Builds an ASN.1 encoded integer in the buffer using as few octets
128 asn_build_int(uchar_t
*buf
, size_t *bufsz_p
, uchar_t id
, int val
)
139 * We need to "pack" the integer before sending it, so determine
140 * the minimum number of bytes in which we can pack the integer
142 uival
= ((uint_t
)val
>> BUILD_INT_SHIFT
) & BUILD_INT_MASK
;
144 sval
= (short)val
; /* yes, loss of data intended */
145 cval
= (char)val
; /* yes, loss of data intended */
147 if (val
== (int)cval
)
149 else if (val
== (int)sval
)
151 else if (uival
== BUILD_INT_MASK
|| uival
== 0)
157 * Prepare the ASN.1 header for the integer
159 if ((p
= asn_build_header(buf
, bufsz_p
, id
, valsz
)) == NULL
)
163 * If we have enough space left, encode the integer
165 if (*bufsz_p
< valsz
)
168 valp
= (uchar_t
*)&ival
;
169 for (i
= 0; i
< valsz
; i
++)
170 p
[i
] = valp
[sizeof (int) - valsz
+ i
];
174 LOGASNINT(buf
, p
+ valsz
- buf
);
180 * Builds an ASN.1 encoded octet string in the buffer. The source string
181 * need not be null-terminated.
184 asn_build_string(uchar_t
*buf
, size_t *bufsz_p
, uchar_t id
, uchar_t
*str
,
189 if ((p
= asn_build_header(buf
, bufsz_p
, id
, slen
)) == NULL
)
196 (void) memcpy(p
, str
, slen
);
198 (void) memset(p
, 0, slen
);
203 LOGASNOCTSTR(buf
, p
+ slen
- buf
);
210 * Builds an Object Identifier into the buffer according to the OID
211 * packing and encoding rules.
214 asn_build_objid(uchar_t
*buf
, size_t *bufsz_p
, uchar_t id
, void *oidp
,
219 oid subid
, first_subid
;
220 uchar_t subid_len
[MAX_SUBIDS_IN_OID
];
225 * Eliminate invalid cases
227 if (n_subids
< MIN_SUBIDS_IN_OID
|| n_subids
> MAX_SUBIDS_IN_OID
)
229 if ((objid
[0] > 2) || (objid
[0] < 2 && objid
[1] >= 40))
233 * The BER encoding rule for the ASN.1 Object Identifier states
234 * that after packing the first two subids into one, each subsequent
235 * component is considered as the next subid. Each subidentifier is
236 * then encoded as a non-negative integer using as few 7-bit blocks
237 * as possible. The blocks are packed in octets with the first bit of
238 * each octet equal to 1, except for the last octet of each subid.
241 for (i
= 0, ndx
= 0; i
< n_subids
; i
++, ndx
++) {
244 * The packing formula for the first two subids
245 * of an OID is given by Z = (X * 40) + Y
247 subid
= objid
[0] * 40 + objid
[1];
249 i
++; /* done with both subids 0 and 1 */
254 if (subid
< (oid
) 0x80)
256 else if (subid
< (oid
) 0x4000)
258 else if (subid
< (oid
) 0x200000)
260 else if (subid
< (oid
) 0x10000000)
266 oid_asnlen
+= subid_len
[ndx
];
269 if ((p
= asn_build_header(buf
, bufsz_p
, id
, oid_asnlen
)) == NULL
)
272 if (*bufsz_p
< oid_asnlen
)
276 * Store the encoded OID
278 for (i
= 0, ndx
= 0; i
< n_subids
; i
++, ndx
++) {
286 switch (subid_len
[ndx
]) {
288 *p
++ = (uchar_t
)subid
;
292 *p
++ = (uchar_t
)((subid
>> 7) | 0x80);
293 *p
++ = (uchar_t
)(subid
& 0x7f);
297 *p
++ = (uchar_t
)((subid
>> 14) | 0x80);
298 *p
++ = (uchar_t
)(((subid
>> 7) & 0x7f) | 0x80);
299 *p
++ = (uchar_t
)(subid
& 0x7f);
303 *p
++ = (uchar_t
)((subid
>> 21) | 0x80);
304 *p
++ = (uchar_t
)(((subid
>> 14) & 0x7f) | 0x80);
305 *p
++ = (uchar_t
)(((subid
>> 7) & 0x7f) | 0x80);
306 *p
++ = (uchar_t
)(subid
& 0x7f);
310 *p
++ = (uchar_t
)((subid
>> 28) | 0x80);
311 *p
++ = (uchar_t
)(((subid
>> 21) & 0x7f) | 0x80);
312 *p
++ = (uchar_t
)(((subid
>> 14) & 0x7f) | 0x80);
313 *p
++ = (uchar_t
)(((subid
>> 7) & 0x7f) | 0x80);
314 *p
++ = (uchar_t
)(subid
& 0x7f);
319 *bufsz_p
-= oid_asnlen
;
321 LOGASNOID(buf
, p
- buf
);
326 * Build an ASN_NULL object val into the request packet
329 asn_build_null(uchar_t
*buf
, size_t *bufsz_p
, uchar_t id
)
333 p
= asn_build_header(buf
, bufsz_p
, id
, 0);
335 LOGASNNULL(buf
, p
- buf
);
343 * This routine parses a 'SEQUENCE OF' object header from the input
344 * buffer stream. If the identifier tag (made up of class, constructed
345 * type and data type tag) does not match the expected identifier tag,
349 asn_parse_sequence(uchar_t
*buf
, size_t *bufsz_p
, uchar_t exp_id
)
354 if ((p
= asn_parse_header(buf
, bufsz_p
, &id
)) == NULL
)
363 * Return the type identifier of the ASN object via 'id'
366 asn_parse_header(uchar_t
*buf
, size_t *bufsz_p
, uchar_t
*id
)
369 size_t asnobj_len
, hdrlen
;
372 * Objects with extension tag type are not supported
374 if ((buf
[0] & ASN_EXT_TAG
) == ASN_EXT_TAG
)
378 * Parse the length field of the ASN object in the header
380 if ((p
= asn_parse_length(buf
+ 1, &asnobj_len
)) == NULL
)
384 * Check if the rest of the msg packet is big enough for the
385 * full length of the object
388 if (*bufsz_p
< (asnobj_len
+ hdrlen
))
397 * This routine parses the length of the object as specified in its
398 * header. The 'Indefinite' form of representing length is not supported.
401 asn_parse_length(uchar_t
*buf
, size_t *asnobj_len_p
)
407 * First, check for the short-definite form. Length of
408 * the object is simply the least significant 7-bits of
411 if ((buf
[0] & ASN_LONG_LEN
) == 0) {
412 *asnobj_len_p
= (size_t)buf
[0];
417 * Then, eliminate the indefinite form. The ASN_LONG_LEN
418 * bit of the first byte will be set and the least significant
419 * 7-bites of that byte will be zeros.
421 if (buf
[0] == (uchar_t
)ASN_LONG_LEN
)
425 * Then, eliminate the long-definite case when the number of
426 * follow-up octets is more than what the size var can hold.
428 n_length_octets
= buf
[0] & ~ASN_LONG_LEN
;
429 if (n_length_octets
> sizeof (*asnobj_len_p
))
433 * Finally gather the length
437 while (n_length_octets
--) {
439 *asnobj_len_p
|= *p
++;
445 * Parses an integer out of the input buffer
448 asn_parse_int(uchar_t
*buf
, size_t *bufsz_p
, int *ival
)
450 size_t asnobj_len
, hdrlen
;
454 int_id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_INTEGER
;
455 if (buf
[0] != int_id
)
459 * Read in the length of the object; Note that integers are
460 * "packed" when sent from agent to manager and vice-versa,
461 * so the size of the object could be less than sizeof (int).
463 if ((p
= asn_parse_length(buf
+ 1, &asnobj_len
)) == NULL
)
467 * Is there sufficient space left in the packet to read the integer ?
470 if (*bufsz_p
< (hdrlen
+ asnobj_len
))
474 * Update space left in the buffer after the integer is read
476 *bufsz_p
-= (hdrlen
+ asnobj_len
);
479 * Read in the integer value
481 *ival
= (*p
& ASN_BIT8
) ? -1 : 0;
482 while (asnobj_len
--) {
490 * Parses an unsigned integer out of the input buffer
493 asn_parse_uint(uchar_t
*buf
, size_t *bufsz_p
, uint_t
*uival
)
495 size_t asnobj_len
, hdrlen
;
498 if ((buf
[0] != ASN_COUNTER
) && (buf
[0] != ASN_TIMETICKS
))
502 * Read in the length of the object. Integers are sent the same
503 * way unsigned integers are sent. Except that, if the MSB was 1
504 * in the unsigned int value, a null-byte is attached to the front.
505 * Otherwise, packing rules are the same as for integer values.
507 if ((p
= asn_parse_length(buf
+ 1, &asnobj_len
)) == NULL
)
511 * Is there sufficient space left in the packet to read in the value ?
514 if (*bufsz_p
< (hdrlen
+ asnobj_len
))
518 * Update space left in the buffer after the uint is read
520 *bufsz_p
-= (hdrlen
+ asnobj_len
);
523 * Read in the unsigned integer (this should never get
524 * initialized to ~0 if it was sent right)
526 *uival
= (*p
& ASN_BIT8
) ? ~0 : 0;
527 while (asnobj_len
--) {
535 * Parses a string (ASN_OCTET_STR or ASN_BIT_STR) out of the input buffer.
536 * The memory for the string is allocated inside the routine and must be
537 * freed by the caller when it is no longer needed. If the string type is
538 * ASN_OCTET_STR, the returned string is null-terminated, and the returned
539 * length indicates the strlen value. If the string type is ASN_BIT_STR,
540 * the returned string is not null-terminated, and the returned length
541 * indicates the number of bytes.
544 asn_parse_string(uchar_t
*buf
, size_t *bufsz_p
, uchar_t
**str_p
, size_t *slen
)
548 size_t asnobj_len
, hdrlen
;
551 * Octet and bit strings are supported
553 id1
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_OCTET_STR
;
554 id2
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_BIT_STR
;
555 if ((buf
[0] != id1
) && (buf
[0] != id2
))
559 * Parse out the length of the object and verify source buf sz
561 if ((p
= asn_parse_length(buf
+ 1, &asnobj_len
)) == NULL
)
565 if (*bufsz_p
< (hdrlen
+ asnobj_len
))
569 * Allocate for and copy out the string
571 if ((*str_p
= (uchar_t
*)calloc(1, asnobj_len
+ 1)) == NULL
)
574 (void) memcpy(*str_p
, p
, asnobj_len
);
577 * Terminate the octet string with a null
580 (*str_p
)[asnobj_len
] = 0;
584 * Update pointers and return
587 *bufsz_p
-= (hdrlen
+ asnobj_len
);
589 return (p
+ asnobj_len
);
592 * Parses an object identifier out of the input packet buffer. Space for
593 * the oid object is allocated within this routine and must be freed by the
594 * caller when no longer needed.
597 asn_parse_objid(uchar_t
*msg
, size_t *varsz_p
, void *oidp
, size_t *n_subids
)
599 oid
**objid_p
= oidp
;
602 size_t hdrlen
, asnobj_len
;
610 exp_id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_OBJECT_ID
;
611 if (msg
[0] != exp_id
)
617 if ((p
= asn_parse_length(msg
+ 1, &asnobj_len
)) == NULL
)
621 * Check space in input message
624 if (*varsz_p
< (hdrlen
+ asnobj_len
))
628 * Since the OID subidentifiers are packed in 7-bit blocks with
629 * MSB set to 1 for all but the last octet, the number of subids
630 * is simply the number of octets with MSB equal to 0, plus 1
631 * (since the first two subids were packed into one subid and have
632 * to be expanded back to two).
635 for (i
= 0; i
< asnobj_len
; i
++) {
636 if ((p
[i
] & ASN_BIT8
) == 0)
641 * Now allocate for the oid and parse the OID into it
643 if ((objid
= (oid
*) calloc(1, (*n_subids
) * sizeof (oid
))) == NULL
)
646 ndx
= 1; /* start from 1 to allow for unpacking later */
648 for (i
= 0; i
< asnobj_len
; i
++) {
650 subid
|= (p
[i
] & ~ASN_BIT8
);
652 if ((p
[i
] & ASN_BIT8
) == 0) {
660 * Now unpack the first two subids from the subid at index 1.
664 } else if (objid
[1] < 80) {
673 *varsz_p
-= (hdrlen
+ asnobj_len
);
675 return (msg
+ hdrlen
+ asnobj_len
);
678 * Parses the value of an OID object out of the input message buffer.
679 * Only type tags less than ASN_EXT_TAG (0x1f) are supported.
682 asn_parse_objval(uchar_t
*msg
, size_t *varsz_p
, void *varlistp
)
684 pdu_varlist_t
*vp
= varlistp
;
687 size_t hdrlen
, asnobj_len
;
689 vp
->type
= msg
[0] & ASN_EXT_TAG
;
690 if (vp
->type
== ASN_EXT_TAG
)
694 * Currently we handle ASN_INTEGER, ASN_OCTET_STR, ASN_BIT_STR
695 * and ASN_TIMETICKS types.
698 case ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_INTEGER
:
699 vp
->val
.iptr
= (int *)calloc(1, sizeof (int));
700 if (vp
->val
.iptr
== NULL
)
703 if ((p
= asn_parse_int(msg
, varsz_p
, vp
->val
.iptr
)) == NULL
) {
707 vp
->val_len
= sizeof (int);
712 vp
->val
.uiptr
= (uint_t
*)calloc(1, sizeof (uint_t
));
713 if (vp
->val
.uiptr
== NULL
)
716 if ((p
= asn_parse_uint(msg
, varsz_p
, vp
->val
.uiptr
)) == NULL
) {
720 vp
->val_len
= sizeof (uint_t
);
724 case ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_OCTET_STR
:
725 case ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_BIT_STR
:
726 p
= asn_parse_string(msg
, varsz_p
, &vp
->val
.str
, &vp
->val_len
);
731 case ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_OBJECT_ID
:
732 p
= asn_parse_objid(msg
, varsz_p
, &vp
->val
.objid
, &n_subids
);
735 vp
->val_len
= n_subids
* sizeof (oid
);
738 case ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_NULL
:
739 case SNMP_NOSUCHOBJECT
:
740 case SNMP_NOSUCHINSTANCE
:
741 case SNMP_ENDOFMIBVIEW
:
743 p
= asn_parse_length(msg
+ 1, &asnobj_len
);
748 if (*varsz_p
< (hdrlen
+ asnobj_len
))
752 vp
->val_len
= asnobj_len
;
754 *varsz_p
-= (hdrlen
+ asnobj_len
);