xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * ASN.1 encoding related routines
31  */
32 
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"
40 
41 /*
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.
45  */
46 uchar_t *
47 asn_build_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
48 {
49 	/*
50 	 * When rebuilding sequence (which we do many times), we'll
51 	 * simply pass NULL to bufsz_p to skip the error check.
52 	 */
53 	if ((bufsz_p) && (*bufsz_p < 4))
54 		return (NULL);
55 
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);
60 
61 	if (bufsz_p)
62 		*bufsz_p -= 4;
63 
64 	LOGASNSEQ(buf, 4);
65 
66 	return (buf + 4);
67 }
68 
69 /*
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.
73  */
74 uchar_t *
75 asn_build_header(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
76 {
77 	if (*bufsz_p < 1)
78 		return (NULL);
79 
80 	buf[0] = id;
81 	(*bufsz_p)--;
82 
83 	return (asn_build_length(buf + 1, bufsz_p, length));
84 }
85 uchar_t *
86 asn_build_length(uchar_t *buf, size_t *bufsz_p, size_t length)
87 {
88 	if (length < 0x80) {
89 		if (*bufsz_p < 1)
90 			return (NULL);
91 		buf[0] = (uchar_t)length;
92 		(*bufsz_p)--;
93 
94 		LOGASNLENGTH(buf, 1);
95 
96 		return (buf + 1);
97 
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;
104 
105 		LOGASNLENGTH(buf, 2);
106 
107 		return (buf + 2);
108 
109 	} else {
110 		if (*bufsz_p < 3)
111 			return (NULL);
112 
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;
117 
118 		LOGASNLENGTH(buf, 3);
119 
120 		return (buf + 3);
121 	}
122 }
123 /*
124  * Builds an ASN.1 encoded integer in the buffer using as few octets
125  * as possible.
126  */
127 uchar_t *
128 asn_build_int(uchar_t *buf, size_t *bufsz_p, uchar_t id, int val)
129 {
130 	uint_t	uival;
131 	int	ival, i;
132 	short	sval;
133 	char	cval;
134 
135 	size_t	valsz;
136 	uchar_t	*p, *valp;
137 
138 	/*
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
141 	 */
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 */
146 
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;
155 
156 	/*
157 	 * Prepare the ASN.1 header for the integer
158 	 */
159 	if ((p = asn_build_header(buf, bufsz_p, id, valsz)) == NULL)
160 		return (NULL);
161 
162 	/*
163 	 * If we have enough space left, encode the integer
164 	 */
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];
171 
172 		*bufsz_p -= valsz;
173 
174 		LOGASNINT(buf, p + valsz - buf);
175 
176 		return (p + valsz);
177 	}
178 }
179 /*
180  * Builds an ASN.1 encoded octet string in the buffer. The source string
181  * need not be null-terminated.
182  */
183 uchar_t *
184 asn_build_string(uchar_t *buf, size_t *bufsz_p, uchar_t id, uchar_t *str,
185     size_t slen)
186 {
187 	uchar_t	*p;
188 
189 	if ((p = asn_build_header(buf, bufsz_p, id, slen)) == NULL)
190 		return (NULL);
191 
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);
199 	    }
200 
201 	    *bufsz_p -= slen;
202 
203 	    LOGASNOCTSTR(buf, p + slen - buf);
204 
205 	    return (p + slen);
206 	}
207 }
208 
209 /*
210  * Builds an Object Identifier into the buffer according to the OID
211  * packing and encoding rules.
212  */
213 uchar_t *
214 asn_build_objid(uchar_t *buf, size_t *bufsz_p, uchar_t id, void *oidp,
215     size_t n_subids)
216 {
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;
223 
224 	/*
225 	 * Eliminate invalid cases
226 	 */
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);
231 
232 	/*
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.
239 	 */
240 	oid_asnlen = 0;
241 	for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
242 		if (i == 0) {
243 			/*
244 			 * The packing formula for the first two subids
245 			 * of an OID is given by Z = (X * 40) + Y
246 			 */
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];
252 		}
253 
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;
264 		}
265 
266 		oid_asnlen += subid_len[ndx];
267 	}
268 
269 	if ((p = asn_build_header(buf, bufsz_p, id, oid_asnlen)) == NULL)
270 		return (NULL);
271 
272 	if (*bufsz_p < oid_asnlen)
273 		return (NULL);
274 
275 	/*
276 	 * Store the encoded OID
277 	 */
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];
284 		}
285 
286 		switch (subid_len[ndx]) {
287 		case 1:
288 			*p++ = (uchar_t)subid;
289 			break;
290 
291 		case 2:
292 			*p++ = (uchar_t)((subid >> 7) | 0x80);
293 			*p++ = (uchar_t)(subid & 0x7f);
294 			break;
295 
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;
301 
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;
308 
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;
316 		}
317 	}
318 
319 	*bufsz_p -= oid_asnlen;
320 
321 	LOGASNOID(buf, p - buf);
322 
323 	return (p);
324 }
325 /*
326  * Build an ASN_NULL object val into the request packet
327  */
328 uchar_t *
329 asn_build_null(uchar_t *buf, size_t *bufsz_p, uchar_t id)
330 {
331 	uchar_t	*p;
332 
333 	p = asn_build_header(buf, bufsz_p, id, 0);
334 
335 	LOGASNNULL(buf, p - buf);
336 
337 	return (p);
338 }
339 
340 
341 
342 /*
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.
347  */
348 uchar_t *
349 asn_parse_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t exp_id)
350 {
351 	uchar_t	*p;
352 	uchar_t	id;
353 
354 	if ((p = asn_parse_header(buf, bufsz_p, &id)) == NULL)
355 		return (NULL);
356 
357 	if (id != exp_id)
358 		return (NULL);
359 
360 	return (p);
361 }
362 /*
363  * Return the type identifier of the ASN object via 'id'
364  */
365 uchar_t *
366 asn_parse_header(uchar_t *buf, size_t *bufsz_p, uchar_t *id)
367 {
368 	uchar_t	*p;
369 	size_t	asnobj_len, hdrlen;
370 
371 	/*
372 	 * Objects with extension tag type are not supported
373 	 */
374 	if ((buf[0] & ASN_EXT_TAG) == ASN_EXT_TAG)
375 		return (NULL);
376 
377 	/*
378 	 * Parse the length field of the ASN object in the header
379 	 */
380 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
381 		return (NULL);
382 
383 	/*
384 	 * Check if the rest of the msg packet is big enough for the
385 	 * full length of the object
386 	 */
387 	hdrlen = p - buf;
388 	if (*bufsz_p < (asnobj_len + hdrlen))
389 		return (NULL);
390 
391 	*id = buf[0];
392 	*bufsz_p -= hdrlen;
393 
394 	return (p);
395 }
396 /*
397  * This routine parses the length of the object as specified in its
398  * header. The 'Indefinite' form of representing length is not supported.
399  */
400 uchar_t *
401 asn_parse_length(uchar_t *buf, size_t *asnobj_len_p)
402 {
403 	uchar_t	*p;
404 	int	n_length_octets;
405 
406 	/*
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.
410 	 */
411 	if ((buf[0] & ASN_LONG_LEN) == 0) {
412 		*asnobj_len_p = (size_t)buf[0];
413 		return (buf + 1);
414 	}
415 
416 	/*
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.
420 	 */
421 	if (buf[0] == (uchar_t)ASN_LONG_LEN)
422 		return (NULL);
423 
424 	/*
425 	 * Then, eliminate the long-definite case when the number of
426 	 * follow-up octets is more than what the size var can hold.
427 	 */
428 	n_length_octets = buf[0] & ~ASN_LONG_LEN;
429 	if (n_length_octets > sizeof (*asnobj_len_p))
430 		return (NULL);
431 
432 	/*
433 	 * Finally gather the length
434 	 */
435 	p = buf + 1;
436 	*asnobj_len_p = 0;
437 	while (n_length_octets--) {
438 		*asnobj_len_p <<= 8;
439 		*asnobj_len_p |= *p++;
440 	}
441 
442 	return (p);
443 }
444 /*
445  * Parses an integer out of the input buffer
446  */
447 uchar_t *
448 asn_parse_int(uchar_t *buf, size_t *bufsz_p, int *ival)
449 {
450 	size_t	asnobj_len, hdrlen;
451 	uchar_t	int_id;
452 	uchar_t	*p;
453 
454 	int_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
455 	if (buf[0] != int_id)
456 		return (NULL);
457 
458 	/*
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).
462 	 */
463 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
464 		return (NULL);
465 
466 	/*
467 	 * Is there sufficient space left in the packet to read the integer ?
468 	 */
469 	hdrlen = p - buf;
470 	if (*bufsz_p < (hdrlen + asnobj_len))
471 		return (NULL);
472 
473 	/*
474 	 * Update space left in the buffer after the integer is read
475 	 */
476 	*bufsz_p -= (hdrlen + asnobj_len);
477 
478 	/*
479 	 * Read in the integer value
480 	 */
481 	*ival = (*p & ASN_BIT8) ? -1 : 0;
482 	while (asnobj_len--) {
483 		*ival <<= 8;
484 		*ival |= *p++;
485 	}
486 
487 	return (p);
488 }
489 /*
490  * Parses an unsigned integer out of the input buffer
491  */
492 uchar_t *
493 asn_parse_uint(uchar_t *buf, size_t *bufsz_p, uint_t *uival)
494 {
495 	size_t	asnobj_len, hdrlen;
496 	uchar_t	*p;
497 
498 	if ((buf[0] != ASN_COUNTER) && (buf[0] != ASN_TIMETICKS))
499 		return (NULL);
500 
501 	/*
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.
506 	 */
507 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
508 		return (NULL);
509 
510 	/*
511 	 * Is there sufficient space left in the packet to read in the value ?
512 	 */
513 	hdrlen = p - buf;
514 	if (*bufsz_p < (hdrlen + asnobj_len))
515 		return (NULL);
516 
517 	/*
518 	 * Update space left in the buffer after the uint is read
519 	 */
520 	*bufsz_p -= (hdrlen + asnobj_len);
521 
522 	/*
523 	 * Read in the unsigned integer (this should never get
524 	 * initialized to ~0 if it was sent right)
525 	 */
526 	*uival = (*p & ASN_BIT8) ? ~0 : 0;
527 	while (asnobj_len--) {
528 		*uival <<= 8;
529 		*uival |= *p++;
530 	}
531 
532 	return (p);
533 }
534 /*
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.
542  */
543 uchar_t *
544 asn_parse_string(uchar_t *buf, size_t *bufsz_p, uchar_t **str_p, size_t *slen)
545 {
546 	uchar_t	*p;
547 	uchar_t	id1, id2;
548 	size_t	asnobj_len, hdrlen;
549 
550 	/*
551 	 * Octet and bit strings are supported
552 	 */
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);
557 
558 	/*
559 	 * Parse out the length of the object and verify source buf sz
560 	 */
561 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
562 		return (NULL);
563 
564 	hdrlen = p - buf;
565 	if (*bufsz_p < (hdrlen + asnobj_len))
566 		return (NULL);
567 
568 	/*
569 	 * Allocate for and copy out the string
570 	 */
571 	if ((*str_p = (uchar_t *)calloc(1, asnobj_len + 1)) == NULL)
572 		return (NULL);
573 
574 	(void) memcpy(*str_p, p, asnobj_len);
575 
576 	/*
577 	 * Terminate the octet string with a null
578 	 */
579 	if (buf[0] == id1) {
580 		(*str_p)[asnobj_len] = 0;
581 	}
582 
583 	/*
584 	 * Update pointers and return
585 	 */
586 	*slen = asnobj_len;
587 	*bufsz_p -= (hdrlen + asnobj_len);
588 
589 	return (p + asnobj_len);
590 }
591 /*
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.
595  */
596 uchar_t *
597 asn_parse_objid(uchar_t *msg, size_t *varsz_p, void *oidp, size_t *n_subids)
598 {
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;
606 
607 	/*
608 	 * Check id
609 	 */
610 	exp_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
611 	if (msg[0] != exp_id)
612 		return (NULL);
613 
614 	/*
615 	 * Read object length
616 	 */
617 	if ((p = asn_parse_length(msg + 1, &asnobj_len)) == NULL)
618 		return (NULL);
619 
620 	/*
621 	 * Check space in input message
622 	 */
623 	hdrlen = p - msg;
624 	if (*varsz_p < (hdrlen + asnobj_len))
625 		return (NULL);
626 
627 	/*
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).
633 	 */
634 	*n_subids = 1;
635 	for (i = 0; i < asnobj_len; i++) {
636 		if ((p[i] & ASN_BIT8) == 0)
637 			(*n_subids)++;
638 	}
639 
640 	/*
641 	 * Now allocate for the oid and parse the OID into it
642 	 */
643 	if ((objid = (oid *) calloc(1, (*n_subids) * sizeof (oid))) == NULL)
644 		return (NULL);
645 
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);
651 
652 		if ((p[i] & ASN_BIT8) == 0) {
653 			objid[ndx] = subid;
654 			ndx++;
655 			subid = 0;
656 		}
657 	}
658 
659 	/*
660 	 * Now unpack the first two subids from the subid at index 1.
661 	 */
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;
670 	}
671 
672 	*objid_p = objid;
673 	*varsz_p -= (hdrlen + asnobj_len);
674 
675 	return (msg + hdrlen + asnobj_len);
676 }
677 /*
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.
680  */
681 uchar_t *
682 asn_parse_objval(uchar_t *msg, size_t *varsz_p, void *varlistp)
683 {
684 	pdu_varlist_t	*vp = varlistp;
685 	uchar_t	*p;
686 	size_t	n_subids;
687 	size_t	hdrlen, asnobj_len;
688 
689 	vp->type = msg[0] & ASN_EXT_TAG;
690 	if (vp->type == ASN_EXT_TAG)
691 		return (NULL);
692 
693 	/*
694 	 * Currently we handle ASN_INTEGER, ASN_OCTET_STR, ASN_BIT_STR
695 	 * and ASN_TIMETICKS types.
696 	 */
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);
702 
703 		if ((p = asn_parse_int(msg, varsz_p, vp->val.iptr)) == NULL) {
704 			free(vp->val.iptr);
705 			return (NULL);
706 		}
707 		vp->val_len = sizeof (int);
708 		break;
709 
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);
715 
716 		if ((p = asn_parse_uint(msg, varsz_p, vp->val.uiptr)) == NULL) {
717 			free(vp->val.uiptr);
718 			return (NULL);
719 		}
720 		vp->val_len = sizeof (uint_t);
721 		vp->type = msg[0];
722 		break;
723 
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;
730 
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;
737 
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);
746 
747 		hdrlen = p - msg;
748 		if (*varsz_p < (hdrlen + asnobj_len))
749 			return (NULL);
750 
751 		vp->type = msg[0];
752 		vp->val_len = asnobj_len;
753 
754 		*varsz_p -= (hdrlen + asnobj_len);
755 		p += asnobj_len;
756 		break;
757 	}
758 
759 	return (p);
760 }
761