xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4v/lib/snmp/asn1.c (revision 78801af7286cd73dbc996d470f789e75993cf15d)
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  * Copyright 2019 Peter Tribble.
26  */
27 
28 /*
29  * ASN.1 encoding related routines
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include "asn1.h"
37 #include "pdu.h"
38 
39 /*
40  * This routine builds a 'SEQUENCE OF' ASN.1 object in the buffer
41  * using the 'id' and 'length' supplied. This is probably the place
42  * where using "reverse" asn encoding will help.
43  */
44 uchar_t *
45 asn_build_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
46 {
47 	/*
48 	 * When rebuilding sequence (which we do many times), we'll
49 	 * simply pass NULL to bufsz_p to skip the error check.
50 	 */
51 	if ((bufsz_p) && (*bufsz_p < 4))
52 		return (NULL);
53 
54 	buf[0] = id;
55 	buf[1] = (uchar_t)(ASN_LONG_LEN | 0x02);	/* following 2 octets */
56 	buf[2] = (uchar_t)((length >> 8) & 0xff);
57 	buf[3] = (uchar_t)(length & 0xff);
58 
59 	if (bufsz_p)
60 		*bufsz_p -= 4;
61 
62 	return (buf + 4);
63 }
64 
65 /*
66  * The next two routines, asn_build_header() and asn_build_length(), build
67  * the header and length for an arbitrary object type into the buffer. The
68  * length of the object is encoded using as few length octets as possible.
69  */
70 uchar_t *
71 asn_build_header(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
72 {
73 	if (*bufsz_p < 1)
74 		return (NULL);
75 
76 	buf[0] = id;
77 	(*bufsz_p)--;
78 
79 	return (asn_build_length(buf + 1, bufsz_p, length));
80 }
81 uchar_t *
82 asn_build_length(uchar_t *buf, size_t *bufsz_p, size_t length)
83 {
84 	if (length < 0x80) {
85 		if (*bufsz_p < 1)
86 			return (NULL);
87 		buf[0] = (uchar_t)length;
88 		(*bufsz_p)--;
89 
90 		return (buf + 1);
91 
92 	} else if (length <= 0xFF) {
93 		if (*bufsz_p < 2)
94 			return (NULL);
95 		buf[0] = (uchar_t)(ASN_LONG_LEN | 0x01);
96 		buf[1] = (uchar_t)length;
97 		*bufsz_p -= 2;
98 
99 		return (buf + 2);
100 
101 	} else {
102 		if (*bufsz_p < 3)
103 			return (NULL);
104 
105 		buf[0] = (uchar_t)(ASN_LONG_LEN | 0x02);
106 		buf[1] = (uchar_t)((length >> 8) & 0xff);
107 		buf[2] = (uchar_t)(length & 0xff);
108 		*bufsz_p -= 3;
109 
110 		return (buf + 3);
111 	}
112 }
113 /*
114  * Builds an ASN.1 encoded integer in the buffer using as few octets
115  * as possible.
116  */
117 uchar_t *
118 asn_build_int(uchar_t *buf, size_t *bufsz_p, uchar_t id, int val)
119 {
120 	uint_t	uival;
121 	int	ival, i;
122 	short	sval;
123 	char	cval;
124 
125 	size_t	valsz;
126 	uchar_t	*p, *valp;
127 
128 	/*
129 	 * We need to "pack" the integer before sending it, so determine
130 	 * the minimum number of bytes in which we can pack the integer
131 	 */
132 	uival = ((uint_t)val >> BUILD_INT_SHIFT) & BUILD_INT_MASK;
133 	ival = val;
134 	sval = (short)val;	/* yes, loss of data intended */
135 	cval = (char)val;	/* yes, loss of data intended */
136 
137 	if (val == (int)cval)
138 		valsz = 1;
139 	else if (val == (int)sval)
140 		valsz = 2;
141 	else if (uival == BUILD_INT_MASK || uival == 0)
142 		valsz = 3;
143 	else
144 		valsz = 4;
145 
146 	/*
147 	 * Prepare the ASN.1 header for the integer
148 	 */
149 	if ((p = asn_build_header(buf, bufsz_p, id, valsz)) == NULL)
150 		return (NULL);
151 
152 	/*
153 	 * If we have enough space left, encode the integer
154 	 */
155 	if (*bufsz_p < valsz)
156 		return (NULL);
157 	else {
158 		valp = (uchar_t *)&ival;
159 		for (i = 0; i < valsz; i++)
160 			p[i] = valp[sizeof (int) - valsz + i];
161 
162 		*bufsz_p -= valsz;
163 
164 		return (p + valsz);
165 	}
166 }
167 /*
168  * Builds an ASN.1 encoded octet string in the buffer. The source string
169  * need not be null-terminated.
170  */
171 uchar_t *
172 asn_build_string(uchar_t *buf, size_t *bufsz_p, uchar_t id, uchar_t *str,
173     size_t slen)
174 {
175 	uchar_t	*p;
176 
177 	if ((p = asn_build_header(buf, bufsz_p, id, slen)) == NULL)
178 		return (NULL);
179 
180 	if (*bufsz_p < slen)
181 		return (NULL);
182 	else {
183 		if (str) {
184 			(void) memcpy(p, str, slen);
185 		} else {
186 			(void) memset(p, 0, slen);
187 		}
188 
189 		*bufsz_p -= slen;
190 
191 		return (p + slen);
192 	}
193 }
194 
195 /*
196  * Builds an Object Identifier into the buffer according to the OID
197  * packing and encoding rules.
198  */
199 uchar_t *
200 asn_build_objid(uchar_t *buf, size_t *bufsz_p, uchar_t id, void *oidp,
201     size_t n_subids)
202 {
203 	oid	*objid = oidp;
204 	size_t	oid_asnlen;
205 	oid	subid, first_subid;
206 	uchar_t	subid_len[MAX_SUBIDS_IN_OID];
207 	uchar_t	*p;
208 	int	i, ndx;
209 
210 	/*
211 	 * Eliminate invalid cases
212 	 */
213 	if (n_subids < MIN_SUBIDS_IN_OID || n_subids > MAX_SUBIDS_IN_OID)
214 		return (NULL);
215 	if ((objid[0] > 2) || (objid[0] < 2 && objid[1] >= 40))
216 		return (NULL);
217 
218 	/*
219 	 * The BER encoding rule for the ASN.1 Object Identifier states
220 	 * that after packing the first two subids into one, each subsequent
221 	 * component is considered as the next subid. Each subidentifier is
222 	 * then encoded as a non-negative integer using as few 7-bit blocks
223 	 * as possible. The blocks are packed in octets with the first bit of
224 	 * each octet equal to 1, except for the last octet of each subid.
225 	 */
226 	oid_asnlen = 0;
227 	for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
228 		if (i == 0) {
229 			/*
230 			 * The packing formula for the first two subids
231 			 * of an OID is given by Z = (X * 40) + Y
232 			 */
233 			subid = objid[0] * 40 + objid[1];
234 			first_subid = subid;
235 			i++;	/* done with both subids 0 and 1 */
236 		} else {
237 			subid = objid[i];
238 		}
239 
240 		if (subid < (oid) 0x80)
241 			subid_len[ndx] = 1;
242 		else if (subid < (oid) 0x4000)
243 			subid_len[ndx] = 2;
244 		else if (subid < (oid) 0x200000)
245 			subid_len[ndx] = 3;
246 		else if (subid < (oid) 0x10000000)
247 			subid_len[ndx] = 4;
248 		else {
249 			subid_len[ndx] = 5;
250 		}
251 
252 		oid_asnlen += subid_len[ndx];
253 	}
254 
255 	if ((p = asn_build_header(buf, bufsz_p, id, oid_asnlen)) == NULL)
256 		return (NULL);
257 
258 	if (*bufsz_p < oid_asnlen)
259 		return (NULL);
260 
261 	/*
262 	 * Store the encoded OID
263 	 */
264 	for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
265 		if (i == 0) {
266 			subid = first_subid;
267 			i++;
268 		} else {
269 			subid = objid[i];
270 		}
271 
272 		switch (subid_len[ndx]) {
273 		case 1:
274 			*p++ = (uchar_t)subid;
275 			break;
276 
277 		case 2:
278 			*p++ = (uchar_t)((subid >> 7) | 0x80);
279 			*p++ = (uchar_t)(subid & 0x7f);
280 			break;
281 
282 		case 3:
283 			*p++ = (uchar_t)((subid >> 14) | 0x80);
284 			*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
285 			*p++ = (uchar_t)(subid & 0x7f);
286 			break;
287 
288 		case 4:
289 			*p++ = (uchar_t)((subid >> 21) | 0x80);
290 			*p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
291 			*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
292 			*p++ = (uchar_t)(subid & 0x7f);
293 			break;
294 
295 		case 5:
296 			*p++ = (uchar_t)((subid >> 28) | 0x80);
297 			*p++ = (uchar_t)(((subid >> 21) & 0x7f) | 0x80);
298 			*p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
299 			*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
300 			*p++ = (uchar_t)(subid & 0x7f);
301 			break;
302 		}
303 	}
304 
305 	*bufsz_p -= oid_asnlen;
306 
307 	return (p);
308 }
309 /*
310  * Build an ASN_NULL object val into the request packet
311  */
312 uchar_t *
313 asn_build_null(uchar_t *buf, size_t *bufsz_p, uchar_t id)
314 {
315 	uchar_t	*p;
316 
317 	p = asn_build_header(buf, bufsz_p, id, 0);
318 
319 	return (p);
320 }
321 
322 
323 
324 /*
325  * This routine parses a 'SEQUENCE OF' object header from the input
326  * buffer stream. If the identifier tag (made up of class, constructed
327  * type and data type tag) does not match the expected identifier tag,
328  * returns failure.
329  */
330 uchar_t *
331 asn_parse_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t exp_id)
332 {
333 	uchar_t	*p;
334 	uchar_t	id;
335 
336 	if ((p = asn_parse_header(buf, bufsz_p, &id)) == NULL)
337 		return (NULL);
338 
339 	if (id != exp_id)
340 		return (NULL);
341 
342 	return (p);
343 }
344 /*
345  * Return the type identifier of the ASN object via 'id'
346  */
347 uchar_t *
348 asn_parse_header(uchar_t *buf, size_t *bufsz_p, uchar_t *id)
349 {
350 	uchar_t	*p;
351 	size_t	asnobj_len, hdrlen;
352 
353 	/*
354 	 * Objects with extension tag type are not supported
355 	 */
356 	if ((buf[0] & ASN_EXT_TAG) == ASN_EXT_TAG)
357 		return (NULL);
358 
359 	/*
360 	 * Parse the length field of the ASN object in the header
361 	 */
362 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
363 		return (NULL);
364 
365 	/*
366 	 * Check if the rest of the msg packet is big enough for the
367 	 * full length of the object
368 	 */
369 	hdrlen = p - buf;
370 	if (*bufsz_p < (asnobj_len + hdrlen))
371 		return (NULL);
372 
373 	*id = buf[0];
374 	*bufsz_p -= hdrlen;
375 
376 	return (p);
377 }
378 /*
379  * This routine parses the length of the object as specified in its
380  * header. The 'Indefinite' form of representing length is not supported.
381  */
382 uchar_t *
383 asn_parse_length(uchar_t *buf, size_t *asnobj_len_p)
384 {
385 	uchar_t	*p;
386 	int	n_length_octets;
387 
388 	/*
389 	 * First, check for the short-definite form. Length of
390 	 * the object is simply the least significant 7-bits of
391 	 * the first byte.
392 	 */
393 	if ((buf[0] & ASN_LONG_LEN) == 0) {
394 		*asnobj_len_p = (size_t)buf[0];
395 		return (buf + 1);
396 	}
397 
398 	/*
399 	 * Then, eliminate the indefinite form. The ASN_LONG_LEN
400 	 * bit of the first byte will be set and the least significant
401 	 * 7-bites of that byte will be zeros.
402 	 */
403 	if (buf[0] == (uchar_t)ASN_LONG_LEN)
404 		return (NULL);
405 
406 	/*
407 	 * Then, eliminate the long-definite case when the number of
408 	 * follow-up octets is more than what the size var can hold.
409 	 */
410 	n_length_octets = buf[0] & ~ASN_LONG_LEN;
411 	if (n_length_octets > sizeof (*asnobj_len_p))
412 		return (NULL);
413 
414 	/*
415 	 * Finally gather the length
416 	 */
417 	p = buf + 1;
418 	*asnobj_len_p = 0;
419 	while (n_length_octets--) {
420 		*asnobj_len_p <<= 8;
421 		*asnobj_len_p |= *p++;
422 	}
423 
424 	return (p);
425 }
426 /*
427  * Parses an integer out of the input buffer
428  */
429 uchar_t *
430 asn_parse_int(uchar_t *buf, size_t *bufsz_p, int *ival)
431 {
432 	size_t	asnobj_len, hdrlen;
433 	uchar_t	int_id;
434 	uchar_t	*p;
435 
436 	int_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
437 	if (buf[0] != int_id)
438 		return (NULL);
439 
440 	/*
441 	 * Read in the length of the object; Note that integers are
442 	 * "packed" when sent from agent to manager and vice-versa,
443 	 * so the size of the object could be less than sizeof (int).
444 	 */
445 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
446 		return (NULL);
447 
448 	/*
449 	 * Is there sufficient space left in the packet to read the integer ?
450 	 */
451 	hdrlen = p - buf;
452 	if (*bufsz_p < (hdrlen + asnobj_len))
453 		return (NULL);
454 
455 	/*
456 	 * Update space left in the buffer after the integer is read
457 	 */
458 	*bufsz_p -= (hdrlen + asnobj_len);
459 
460 	/*
461 	 * Read in the integer value
462 	 */
463 	*ival = (*p & ASN_BIT8) ? -1 : 0;
464 	while (asnobj_len--) {
465 		*ival <<= 8;
466 		*ival |= *p++;
467 	}
468 
469 	return (p);
470 }
471 /*
472  * Parses an unsigned integer out of the input buffer
473  */
474 uchar_t *
475 asn_parse_uint(uchar_t *buf, size_t *bufsz_p, uint_t *uival)
476 {
477 	size_t	asnobj_len, hdrlen;
478 	uchar_t	*p;
479 
480 	if ((buf[0] != ASN_COUNTER) && (buf[0] != ASN_TIMETICKS))
481 		return (NULL);
482 
483 	/*
484 	 * Read in the length of the object. Integers are sent the same
485 	 * way unsigned integers are sent.  Except that, if the MSB was 1
486 	 * in the unsigned int value, a null-byte is attached to the front.
487 	 * Otherwise, packing rules are the same as for integer values.
488 	 */
489 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
490 		return (NULL);
491 
492 	/*
493 	 * Is there sufficient space left in the packet to read in the value ?
494 	 */
495 	hdrlen = p - buf;
496 	if (*bufsz_p < (hdrlen + asnobj_len))
497 		return (NULL);
498 
499 	/*
500 	 * Update space left in the buffer after the uint is read
501 	 */
502 	*bufsz_p -= (hdrlen + asnobj_len);
503 
504 	/*
505 	 * Read in the unsigned integer (this should never get
506 	 * initialized to ~0 if it was sent right)
507 	 */
508 	*uival = (*p & ASN_BIT8) ? ~0 : 0;
509 	while (asnobj_len--) {
510 		*uival <<= 8;
511 		*uival |= *p++;
512 	}
513 
514 	return (p);
515 }
516 /*
517  * Parses a string (ASN_OCTET_STR or ASN_BIT_STR) out of the input buffer.
518  * The memory for the string is allocated inside the routine and must be
519  * freed by the caller when it is no longer needed. If the string type is
520  * ASN_OCTET_STR, the returned string is null-terminated, and the returned
521  * length indicates the strlen value. If the string type is ASN_BIT_STR,
522  * the returned string is not null-terminated, and the returned length
523  * indicates the number of bytes.
524  */
525 uchar_t *
526 asn_parse_string(uchar_t *buf, size_t *bufsz_p, uchar_t **str_p, size_t *slen)
527 {
528 	uchar_t	*p;
529 	uchar_t	id1, id2;
530 	size_t	asnobj_len, hdrlen;
531 
532 	/*
533 	 * Octet and bit strings are supported
534 	 */
535 	id1 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
536 	id2 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR;
537 	if ((buf[0] != id1) && (buf[0] != id2))
538 		return (NULL);
539 
540 	/*
541 	 * Parse out the length of the object and verify source buf sz
542 	 */
543 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
544 		return (NULL);
545 
546 	hdrlen = p - buf;
547 	if (*bufsz_p < (hdrlen + asnobj_len))
548 		return (NULL);
549 
550 	/*
551 	 * Allocate for and copy out the string
552 	 */
553 	if ((*str_p = (uchar_t *)calloc(1, asnobj_len + 1)) == NULL)
554 		return (NULL);
555 
556 	(void) memcpy(*str_p, p, asnobj_len);
557 
558 	/*
559 	 * Terminate the octet string with a null
560 	 */
561 	if (buf[0] == id1) {
562 		(*str_p)[asnobj_len] = 0;
563 	}
564 
565 	/*
566 	 * Update pointers and return
567 	 */
568 	*slen = asnobj_len;
569 	*bufsz_p -= (hdrlen + asnobj_len);
570 
571 	return (p + asnobj_len);
572 }
573 /*
574  * Parses an object identifier out of the input packet buffer. Space for
575  * the oid object is allocated within this routine and must be freed by the
576  * caller when no longer needed.
577  */
578 uchar_t *
579 asn_parse_objid(uchar_t *msg, size_t *varsz_p, void *oidp, size_t *n_subids)
580 {
581 	oid	**objid_p = oidp;
582 	oid	*objid;
583 	uchar_t	*p;
584 	size_t	hdrlen, asnobj_len;
585 	oid	subid;
586 	int	i, ndx;
587 	uchar_t	exp_id;
588 
589 	/*
590 	 * Check id
591 	 */
592 	exp_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
593 	if (msg[0] != exp_id)
594 		return (NULL);
595 
596 	/*
597 	 * Read object length
598 	 */
599 	if ((p = asn_parse_length(msg + 1, &asnobj_len)) == NULL)
600 		return (NULL);
601 
602 	/*
603 	 * Check space in input message
604 	 */
605 	hdrlen = p - msg;
606 	if (*varsz_p < (hdrlen + asnobj_len))
607 		return (NULL);
608 
609 	/*
610 	 * Since the OID subidentifiers are packed in 7-bit blocks with
611 	 * MSB set to 1 for all but the last octet, the number of subids
612 	 * is simply the number of octets with MSB equal to 0, plus 1
613 	 * (since the first two subids were packed into one subid and have
614 	 * to be expanded back to two).
615 	 */
616 	*n_subids = 1;
617 	for (i = 0; i < asnobj_len; i++) {
618 		if ((p[i] & ASN_BIT8) == 0)
619 			(*n_subids)++;
620 	}
621 
622 	/*
623 	 * Now allocate for the oid and parse the OID into it
624 	 */
625 	if ((objid = (oid *) calloc(1, (*n_subids) * sizeof (oid))) == NULL)
626 		return (NULL);
627 
628 	ndx = 1;	/* start from 1 to allow for unpacking later */
629 	subid = 0;
630 	for (i = 0; i < asnobj_len; i++) {
631 		subid = subid << 7;
632 		subid |= (p[i] & ~ASN_BIT8);
633 
634 		if ((p[i] & ASN_BIT8) == 0) {
635 			objid[ndx] = subid;
636 			ndx++;
637 			subid = 0;
638 		}
639 	}
640 
641 	/*
642 	 * Now unpack the first two subids from the subid at index 1.
643 	 */
644 	if (objid[1] < 40) {
645 		objid[0] = 0;
646 	} else if (objid[1] < 80) {
647 		objid[0] = 1;
648 		objid[1] -= 40;
649 	} else {
650 		objid[0] = 2;
651 		objid[1] -= 80;
652 	}
653 
654 	*objid_p = objid;
655 	*varsz_p -= (hdrlen + asnobj_len);
656 
657 	return (msg + hdrlen + asnobj_len);
658 }
659 /*
660  * Parses the value of an OID object out of the input message buffer.
661  * Only type tags less than ASN_EXT_TAG (0x1f) are supported.
662  */
663 uchar_t *
664 asn_parse_objval(uchar_t *msg, size_t *varsz_p, void *varlistp)
665 {
666 	pdu_varlist_t	*vp = varlistp;
667 	uchar_t	*p;
668 	size_t	n_subids;
669 	size_t	hdrlen, asnobj_len;
670 
671 	vp->type = msg[0] & ASN_EXT_TAG;
672 	if (vp->type == ASN_EXT_TAG)
673 		return (NULL);
674 
675 	/*
676 	 * Currently we handle ASN_INTEGER, ASN_OCTET_STR, ASN_BIT_STR
677 	 * and ASN_TIMETICKS types.
678 	 */
679 	switch (msg[0]) {
680 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER:
681 		vp->val.iptr = (int *)calloc(1, sizeof (int));
682 		if (vp->val.iptr == NULL)
683 			return (NULL);
684 
685 		if ((p = asn_parse_int(msg, varsz_p, vp->val.iptr)) == NULL) {
686 			free(vp->val.iptr);
687 			return (NULL);
688 		}
689 		vp->val_len = sizeof (int);
690 		break;
691 
692 	case ASN_COUNTER:
693 	case ASN_TIMETICKS:
694 		vp->val.uiptr = (uint_t *)calloc(1, sizeof (uint_t));
695 		if (vp->val.uiptr == NULL)
696 			return (NULL);
697 
698 		if ((p = asn_parse_uint(msg, varsz_p, vp->val.uiptr)) == NULL) {
699 			free(vp->val.uiptr);
700 			return (NULL);
701 		}
702 		vp->val_len = sizeof (uint_t);
703 		vp->type = msg[0];
704 		break;
705 
706 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR:
707 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR:
708 		p = asn_parse_string(msg, varsz_p, &vp->val.str, &vp->val_len);
709 		if (p == NULL)
710 			return (NULL);
711 		break;
712 
713 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID:
714 		p = asn_parse_objid(msg, varsz_p, &vp->val.objid, &n_subids);
715 		if (p == NULL)
716 			return (NULL);
717 		vp->val_len = n_subids * sizeof (oid);
718 		break;
719 
720 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_NULL:
721 	case SNMP_NOSUCHOBJECT:
722 	case SNMP_NOSUCHINSTANCE:
723 	case SNMP_ENDOFMIBVIEW:
724 	default:
725 		p = asn_parse_length(msg + 1, &asnobj_len);
726 		if (p == NULL)
727 			return (NULL);
728 
729 		hdrlen = p - msg;
730 		if (*varsz_p < (hdrlen + asnobj_len))
731 			return (NULL);
732 
733 		vp->type = msg[0];
734 		vp->val_len = asnobj_len;
735 
736 		*varsz_p -= (hdrlen + asnobj_len);
737 		p += asnobj_len;
738 		break;
739 	}
740 
741 	return (p);
742 }
743