cmd: remove sparc-only virtinfo
[unleashed.git] / usr / src / cmd / picl / plugins / sun4v / lib / snmp / asn1.c
blob4de8545981c629bf5f6896cd9ba9c7070bbfd52b
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include "asn1.h"
38 #include "pdu.h"
39 #include "debug.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.
46 uchar_t *
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))
54 return (NULL);
56 buf[0] = id;
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);
61 if (bufsz_p)
62 *bufsz_p -= 4;
64 LOGASNSEQ(buf, 4);
66 return (buf + 4);
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.
74 uchar_t *
75 asn_build_header(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
77 if (*bufsz_p < 1)
78 return (NULL);
80 buf[0] = id;
81 (*bufsz_p)--;
83 return (asn_build_length(buf + 1, bufsz_p, length));
85 uchar_t *
86 asn_build_length(uchar_t *buf, size_t *bufsz_p, size_t length)
88 if (length < 0x80) {
89 if (*bufsz_p < 1)
90 return (NULL);
91 buf[0] = (uchar_t)length;
92 (*bufsz_p)--;
94 LOGASNLENGTH(buf, 1);
96 return (buf + 1);
98 } else if (length <= 0xFF) {
99 if (*bufsz_p < 2)
100 return (NULL);
101 buf[0] = (uchar_t)(ASN_LONG_LEN | 0x01);
102 buf[1] = (uchar_t)length;
103 *bufsz_p -= 2;
105 LOGASNLENGTH(buf, 2);
107 return (buf + 2);
109 } else {
110 if (*bufsz_p < 3)
111 return (NULL);
113 buf[0] = (uchar_t)(ASN_LONG_LEN | 0x02);
114 buf[1] = (uchar_t)((length >> 8) & 0xff);
115 buf[2] = (uchar_t)(length & 0xff);
116 *bufsz_p -= 3;
118 LOGASNLENGTH(buf, 3);
120 return (buf + 3);
124 * Builds an ASN.1 encoded integer in the buffer using as few octets
125 * as possible.
127 uchar_t *
128 asn_build_int(uchar_t *buf, size_t *bufsz_p, uchar_t id, int val)
130 uint_t uival;
131 int ival, i;
132 short sval;
133 char cval;
135 size_t valsz;
136 uchar_t *p, *valp;
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;
143 ival = val;
144 sval = (short)val; /* yes, loss of data intended */
145 cval = (char)val; /* yes, loss of data intended */
147 if (val == (int)cval)
148 valsz = 1;
149 else if (val == (int)sval)
150 valsz = 2;
151 else if (uival == BUILD_INT_MASK || uival == 0)
152 valsz = 3;
153 else
154 valsz = 4;
157 * Prepare the ASN.1 header for the integer
159 if ((p = asn_build_header(buf, bufsz_p, id, valsz)) == NULL)
160 return (NULL);
163 * If we have enough space left, encode the integer
165 if (*bufsz_p < valsz)
166 return (NULL);
167 else {
168 valp = (uchar_t *)&ival;
169 for (i = 0; i < valsz; i++)
170 p[i] = valp[sizeof (int) - valsz + i];
172 *bufsz_p -= valsz;
174 LOGASNINT(buf, p + valsz - buf);
176 return (p + valsz);
180 * Builds an ASN.1 encoded octet string in the buffer. The source string
181 * need not be null-terminated.
183 uchar_t *
184 asn_build_string(uchar_t *buf, size_t *bufsz_p, uchar_t id, uchar_t *str,
185 size_t slen)
187 uchar_t *p;
189 if ((p = asn_build_header(buf, bufsz_p, id, slen)) == NULL)
190 return (NULL);
192 if (*bufsz_p < slen)
193 return (NULL);
194 else {
195 if (str) {
196 (void) memcpy(p, str, slen);
197 } else {
198 (void) memset(p, 0, slen);
201 *bufsz_p -= slen;
203 LOGASNOCTSTR(buf, p + slen - buf);
205 return (p + slen);
210 * Builds an Object Identifier into the buffer according to the OID
211 * packing and encoding rules.
213 uchar_t *
214 asn_build_objid(uchar_t *buf, size_t *bufsz_p, uchar_t id, void *oidp,
215 size_t n_subids)
217 oid *objid = oidp;
218 size_t oid_asnlen;
219 oid subid, first_subid;
220 uchar_t subid_len[MAX_SUBIDS_IN_OID];
221 uchar_t *p;
222 int i, ndx;
225 * Eliminate invalid cases
227 if (n_subids < MIN_SUBIDS_IN_OID || n_subids > MAX_SUBIDS_IN_OID)
228 return (NULL);
229 if ((objid[0] > 2) || (objid[0] < 2 && objid[1] >= 40))
230 return (NULL);
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.
240 oid_asnlen = 0;
241 for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
242 if (i == 0) {
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];
248 first_subid = subid;
249 i++; /* done with both subids 0 and 1 */
250 } else {
251 subid = objid[i];
254 if (subid < (oid) 0x80)
255 subid_len[ndx] = 1;
256 else if (subid < (oid) 0x4000)
257 subid_len[ndx] = 2;
258 else if (subid < (oid) 0x200000)
259 subid_len[ndx] = 3;
260 else if (subid < (oid) 0x10000000)
261 subid_len[ndx] = 4;
262 else {
263 subid_len[ndx] = 5;
266 oid_asnlen += subid_len[ndx];
269 if ((p = asn_build_header(buf, bufsz_p, id, oid_asnlen)) == NULL)
270 return (NULL);
272 if (*bufsz_p < oid_asnlen)
273 return (NULL);
276 * Store the encoded OID
278 for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
279 if (i == 0) {
280 subid = first_subid;
281 i++;
282 } else {
283 subid = objid[i];
286 switch (subid_len[ndx]) {
287 case 1:
288 *p++ = (uchar_t)subid;
289 break;
291 case 2:
292 *p++ = (uchar_t)((subid >> 7) | 0x80);
293 *p++ = (uchar_t)(subid & 0x7f);
294 break;
296 case 3:
297 *p++ = (uchar_t)((subid >> 14) | 0x80);
298 *p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
299 *p++ = (uchar_t)(subid & 0x7f);
300 break;
302 case 4:
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);
307 break;
309 case 5:
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);
315 break;
319 *bufsz_p -= oid_asnlen;
321 LOGASNOID(buf, p - buf);
323 return (p);
326 * Build an ASN_NULL object val into the request packet
328 uchar_t *
329 asn_build_null(uchar_t *buf, size_t *bufsz_p, uchar_t id)
331 uchar_t *p;
333 p = asn_build_header(buf, bufsz_p, id, 0);
335 LOGASNNULL(buf, p - buf);
337 return (p);
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,
346 * returns failure.
348 uchar_t *
349 asn_parse_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t exp_id)
351 uchar_t *p;
352 uchar_t id;
354 if ((p = asn_parse_header(buf, bufsz_p, &id)) == NULL)
355 return (NULL);
357 if (id != exp_id)
358 return (NULL);
360 return (p);
363 * Return the type identifier of the ASN object via 'id'
365 uchar_t *
366 asn_parse_header(uchar_t *buf, size_t *bufsz_p, uchar_t *id)
368 uchar_t *p;
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)
375 return (NULL);
378 * Parse the length field of the ASN object in the header
380 if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
381 return (NULL);
384 * Check if the rest of the msg packet is big enough for the
385 * full length of the object
387 hdrlen = p - buf;
388 if (*bufsz_p < (asnobj_len + hdrlen))
389 return (NULL);
391 *id = buf[0];
392 *bufsz_p -= hdrlen;
394 return (p);
397 * This routine parses the length of the object as specified in its
398 * header. The 'Indefinite' form of representing length is not supported.
400 uchar_t *
401 asn_parse_length(uchar_t *buf, size_t *asnobj_len_p)
403 uchar_t *p;
404 int n_length_octets;
407 * First, check for the short-definite form. Length of
408 * the object is simply the least significant 7-bits of
409 * the first byte.
411 if ((buf[0] & ASN_LONG_LEN) == 0) {
412 *asnobj_len_p = (size_t)buf[0];
413 return (buf + 1);
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)
422 return (NULL);
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))
430 return (NULL);
433 * Finally gather the length
435 p = buf + 1;
436 *asnobj_len_p = 0;
437 while (n_length_octets--) {
438 *asnobj_len_p <<= 8;
439 *asnobj_len_p |= *p++;
442 return (p);
445 * Parses an integer out of the input buffer
447 uchar_t *
448 asn_parse_int(uchar_t *buf, size_t *bufsz_p, int *ival)
450 size_t asnobj_len, hdrlen;
451 uchar_t int_id;
452 uchar_t *p;
454 int_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
455 if (buf[0] != int_id)
456 return (NULL);
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)
464 return (NULL);
467 * Is there sufficient space left in the packet to read the integer ?
469 hdrlen = p - buf;
470 if (*bufsz_p < (hdrlen + asnobj_len))
471 return (NULL);
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--) {
483 *ival <<= 8;
484 *ival |= *p++;
487 return (p);
490 * Parses an unsigned integer out of the input buffer
492 uchar_t *
493 asn_parse_uint(uchar_t *buf, size_t *bufsz_p, uint_t *uival)
495 size_t asnobj_len, hdrlen;
496 uchar_t *p;
498 if ((buf[0] != ASN_COUNTER) && (buf[0] != ASN_TIMETICKS))
499 return (NULL);
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)
508 return (NULL);
511 * Is there sufficient space left in the packet to read in the value ?
513 hdrlen = p - buf;
514 if (*bufsz_p < (hdrlen + asnobj_len))
515 return (NULL);
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--) {
528 *uival <<= 8;
529 *uival |= *p++;
532 return (p);
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.
543 uchar_t *
544 asn_parse_string(uchar_t *buf, size_t *bufsz_p, uchar_t **str_p, size_t *slen)
546 uchar_t *p;
547 uchar_t id1, id2;
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))
556 return (NULL);
559 * Parse out the length of the object and verify source buf sz
561 if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
562 return (NULL);
564 hdrlen = p - buf;
565 if (*bufsz_p < (hdrlen + asnobj_len))
566 return (NULL);
569 * Allocate for and copy out the string
571 if ((*str_p = (uchar_t *)calloc(1, asnobj_len + 1)) == NULL)
572 return (NULL);
574 (void) memcpy(*str_p, p, asnobj_len);
577 * Terminate the octet string with a null
579 if (buf[0] == id1) {
580 (*str_p)[asnobj_len] = 0;
584 * Update pointers and return
586 *slen = asnobj_len;
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.
596 uchar_t *
597 asn_parse_objid(uchar_t *msg, size_t *varsz_p, void *oidp, size_t *n_subids)
599 oid **objid_p = oidp;
600 oid *objid;
601 uchar_t *p;
602 size_t hdrlen, asnobj_len;
603 oid subid;
604 int i, ndx;
605 uchar_t exp_id;
608 * Check id
610 exp_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
611 if (msg[0] != exp_id)
612 return (NULL);
615 * Read object length
617 if ((p = asn_parse_length(msg + 1, &asnobj_len)) == NULL)
618 return (NULL);
621 * Check space in input message
623 hdrlen = p - msg;
624 if (*varsz_p < (hdrlen + asnobj_len))
625 return (NULL);
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).
634 *n_subids = 1;
635 for (i = 0; i < asnobj_len; i++) {
636 if ((p[i] & ASN_BIT8) == 0)
637 (*n_subids)++;
641 * Now allocate for the oid and parse the OID into it
643 if ((objid = (oid *) calloc(1, (*n_subids) * sizeof (oid))) == NULL)
644 return (NULL);
646 ndx = 1; /* start from 1 to allow for unpacking later */
647 subid = 0;
648 for (i = 0; i < asnobj_len; i++) {
649 subid = subid << 7;
650 subid |= (p[i] & ~ASN_BIT8);
652 if ((p[i] & ASN_BIT8) == 0) {
653 objid[ndx] = subid;
654 ndx++;
655 subid = 0;
660 * Now unpack the first two subids from the subid at index 1.
662 if (objid[1] < 40) {
663 objid[0] = 0;
664 } else if (objid[1] < 80) {
665 objid[0] = 1;
666 objid[1] -= 40;
667 } else {
668 objid[0] = 2;
669 objid[1] -= 80;
672 *objid_p = objid;
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.
681 uchar_t *
682 asn_parse_objval(uchar_t *msg, size_t *varsz_p, void *varlistp)
684 pdu_varlist_t *vp = varlistp;
685 uchar_t *p;
686 size_t n_subids;
687 size_t hdrlen, asnobj_len;
689 vp->type = msg[0] & ASN_EXT_TAG;
690 if (vp->type == ASN_EXT_TAG)
691 return (NULL);
694 * Currently we handle ASN_INTEGER, ASN_OCTET_STR, ASN_BIT_STR
695 * and ASN_TIMETICKS types.
697 switch (msg[0]) {
698 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER:
699 vp->val.iptr = (int *)calloc(1, sizeof (int));
700 if (vp->val.iptr == NULL)
701 return (NULL);
703 if ((p = asn_parse_int(msg, varsz_p, vp->val.iptr)) == NULL) {
704 free(vp->val.iptr);
705 return (NULL);
707 vp->val_len = sizeof (int);
708 break;
710 case ASN_COUNTER:
711 case ASN_TIMETICKS:
712 vp->val.uiptr = (uint_t *)calloc(1, sizeof (uint_t));
713 if (vp->val.uiptr == NULL)
714 return (NULL);
716 if ((p = asn_parse_uint(msg, varsz_p, vp->val.uiptr)) == NULL) {
717 free(vp->val.uiptr);
718 return (NULL);
720 vp->val_len = sizeof (uint_t);
721 vp->type = msg[0];
722 break;
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);
727 if (p == NULL)
728 return (NULL);
729 break;
731 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID:
732 p = asn_parse_objid(msg, varsz_p, &vp->val.objid, &n_subids);
733 if (p == NULL)
734 return (NULL);
735 vp->val_len = n_subids * sizeof (oid);
736 break;
738 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_NULL:
739 case SNMP_NOSUCHOBJECT:
740 case SNMP_NOSUCHINSTANCE:
741 case SNMP_ENDOFMIBVIEW:
742 default:
743 p = asn_parse_length(msg + 1, &asnobj_len);
744 if (p == NULL)
745 return (NULL);
747 hdrlen = p - msg;
748 if (*varsz_p < (hdrlen + asnobj_len))
749 return (NULL);
751 vp->type = msg[0];
752 vp->val_len = asnobj_len;
754 *varsz_p -= (hdrlen + asnobj_len);
755 p += asnobj_len;
756 break;
759 return (p);