10d63ce2bSvenki /*
20d63ce2bSvenki  * CDDL HEADER START
30d63ce2bSvenki  *
40d63ce2bSvenki  * The contents of this file are subject to the terms of the
50d63ce2bSvenki  * Common Development and Distribution License (the "License").
60d63ce2bSvenki  * You may not use this file except in compliance with the License.
70d63ce2bSvenki  *
80d63ce2bSvenki  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90d63ce2bSvenki  * or http://www.opensolaris.org/os/licensing.
100d63ce2bSvenki  * See the License for the specific language governing permissions
110d63ce2bSvenki  * and limitations under the License.
120d63ce2bSvenki  *
130d63ce2bSvenki  * When distributing Covered Code, include this CDDL HEADER in each
140d63ce2bSvenki  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150d63ce2bSvenki  * If applicable, add the following below this CDDL HEADER, with the
160d63ce2bSvenki  * fields enclosed by brackets "[]" replaced with your own identifying
170d63ce2bSvenki  * information: Portions Copyright [yyyy] [name of copyright owner]
180d63ce2bSvenki  *
190d63ce2bSvenki  * CDDL HEADER END
200d63ce2bSvenki  */
210d63ce2bSvenki 
220d63ce2bSvenki /*
230d63ce2bSvenki  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
240d63ce2bSvenki  * Use is subject to license terms.
25*0409f346SPeter Tribble  * Copyright 2019 Peter Tribble.
260d63ce2bSvenki  */
270d63ce2bSvenki 
280d63ce2bSvenki /*
290d63ce2bSvenki  * ASN.1 encoding related routines
300d63ce2bSvenki  */
310d63ce2bSvenki 
320d63ce2bSvenki #include <stdio.h>
330d63ce2bSvenki #include <stdlib.h>
340d63ce2bSvenki #include <string.h>
350d63ce2bSvenki #include <sys/types.h>
360d63ce2bSvenki #include "asn1.h"
370d63ce2bSvenki #include "pdu.h"
380d63ce2bSvenki 
390d63ce2bSvenki /*
400d63ce2bSvenki  * This routine builds a 'SEQUENCE OF' ASN.1 object in the buffer
410d63ce2bSvenki  * using the 'id' and 'length' supplied. This is probably the place
420d63ce2bSvenki  * where using "reverse" asn encoding will help.
430d63ce2bSvenki  */
440d63ce2bSvenki uchar_t *
asn_build_sequence(uchar_t * buf,size_t * bufsz_p,uchar_t id,size_t length)450d63ce2bSvenki asn_build_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
460d63ce2bSvenki {
470d63ce2bSvenki 	/*
480d63ce2bSvenki 	 * When rebuilding sequence (which we do many times), we'll
490d63ce2bSvenki 	 * simply pass NULL to bufsz_p to skip the error check.
500d63ce2bSvenki 	 */
510d63ce2bSvenki 	if ((bufsz_p) && (*bufsz_p < 4))
520d63ce2bSvenki 		return (NULL);
530d63ce2bSvenki 
540d63ce2bSvenki 	buf[0] = id;
550d63ce2bSvenki 	buf[1] = (uchar_t)(ASN_LONG_LEN | 0x02);	/* following 2 octets */
560d63ce2bSvenki 	buf[2] = (uchar_t)((length >> 8) & 0xff);
570d63ce2bSvenki 	buf[3] = (uchar_t)(length & 0xff);
580d63ce2bSvenki 
590d63ce2bSvenki 	if (bufsz_p)
600d63ce2bSvenki 		*bufsz_p -= 4;
610d63ce2bSvenki 
620d63ce2bSvenki 	return (buf + 4);
630d63ce2bSvenki }
640d63ce2bSvenki 
650d63ce2bSvenki /*
660d63ce2bSvenki  * The next two routines, asn_build_header() and asn_build_length(), build
670d63ce2bSvenki  * the header and length for an arbitrary object type into the buffer. The
680d63ce2bSvenki  * length of the object is encoded using as few length octets as possible.
690d63ce2bSvenki  */
700d63ce2bSvenki uchar_t *
asn_build_header(uchar_t * buf,size_t * bufsz_p,uchar_t id,size_t length)710d63ce2bSvenki asn_build_header(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
720d63ce2bSvenki {
730d63ce2bSvenki 	if (*bufsz_p < 1)
740d63ce2bSvenki 		return (NULL);
750d63ce2bSvenki 
760d63ce2bSvenki 	buf[0] = id;
770d63ce2bSvenki 	(*bufsz_p)--;
780d63ce2bSvenki 
790d63ce2bSvenki 	return (asn_build_length(buf + 1, bufsz_p, length));
800d63ce2bSvenki }
810d63ce2bSvenki uchar_t *
asn_build_length(uchar_t * buf,size_t * bufsz_p,size_t length)820d63ce2bSvenki asn_build_length(uchar_t *buf, size_t *bufsz_p, size_t length)
830d63ce2bSvenki {
840d63ce2bSvenki 	if (length < 0x80) {
850d63ce2bSvenki 		if (*bufsz_p < 1)
860d63ce2bSvenki 			return (NULL);
870d63ce2bSvenki 		buf[0] = (uchar_t)length;
880d63ce2bSvenki 		(*bufsz_p)--;
890d63ce2bSvenki 
900d63ce2bSvenki 		return (buf + 1);
910d63ce2bSvenki 
920d63ce2bSvenki 	} else if (length <= 0xFF) {
930d63ce2bSvenki 		if (*bufsz_p < 2)
940d63ce2bSvenki 			return (NULL);
950d63ce2bSvenki 		buf[0] = (uchar_t)(ASN_LONG_LEN | 0x01);
960d63ce2bSvenki 		buf[1] = (uchar_t)length;
970d63ce2bSvenki 		*bufsz_p -= 2;
980d63ce2bSvenki 
990d63ce2bSvenki 		return (buf + 2);
1000d63ce2bSvenki 
1010d63ce2bSvenki 	} else {
1020d63ce2bSvenki 		if (*bufsz_p < 3)
1030d63ce2bSvenki 			return (NULL);
1040d63ce2bSvenki 
1050d63ce2bSvenki 		buf[0] = (uchar_t)(ASN_LONG_LEN | 0x02);
1060d63ce2bSvenki 		buf[1] = (uchar_t)((length >> 8) & 0xff);
1070d63ce2bSvenki 		buf[2] = (uchar_t)(length & 0xff);
1080d63ce2bSvenki 		*bufsz_p -= 3;
1090d63ce2bSvenki 
1100d63ce2bSvenki 		return (buf + 3);
1110d63ce2bSvenki 	}
1120d63ce2bSvenki }
1130d63ce2bSvenki /*
1140d63ce2bSvenki  * Builds an ASN.1 encoded integer in the buffer using as few octets
1150d63ce2bSvenki  * as possible.
1160d63ce2bSvenki  */
1170d63ce2bSvenki uchar_t *
asn_build_int(uchar_t * buf,size_t * bufsz_p,uchar_t id,int val)1180d63ce2bSvenki asn_build_int(uchar_t *buf, size_t *bufsz_p, uchar_t id, int val)
1190d63ce2bSvenki {
1200d63ce2bSvenki 	uint_t	uival;
1210d63ce2bSvenki 	int	ival, i;
1220d63ce2bSvenki 	short	sval;
1230d63ce2bSvenki 	char	cval;
1240d63ce2bSvenki 
1250d63ce2bSvenki 	size_t	valsz;
1260d63ce2bSvenki 	uchar_t	*p, *valp;
1270d63ce2bSvenki 
1280d63ce2bSvenki 	/*
1290d63ce2bSvenki 	 * We need to "pack" the integer before sending it, so determine
1300d63ce2bSvenki 	 * the minimum number of bytes in which we can pack the integer
1310d63ce2bSvenki 	 */
1320d63ce2bSvenki 	uival = ((uint_t)val >> BUILD_INT_SHIFT) & BUILD_INT_MASK;
1330d63ce2bSvenki 	ival = val;
1340d63ce2bSvenki 	sval = (short)val;	/* yes, loss of data intended */
1350d63ce2bSvenki 	cval = (char)val;	/* yes, loss of data intended */
1360d63ce2bSvenki 
1370d63ce2bSvenki 	if (val == (int)cval)
1380d63ce2bSvenki 		valsz = 1;
1390d63ce2bSvenki 	else if (val == (int)sval)
1400d63ce2bSvenki 		valsz = 2;
1410d63ce2bSvenki 	else if (uival == BUILD_INT_MASK || uival == 0)
1420d63ce2bSvenki 		valsz = 3;
1430d63ce2bSvenki 	else
1440d63ce2bSvenki 		valsz = 4;
1450d63ce2bSvenki 
1460d63ce2bSvenki 	/*
1470d63ce2bSvenki 	 * Prepare the ASN.1 header for the integer
1480d63ce2bSvenki 	 */
1490d63ce2bSvenki 	if ((p = asn_build_header(buf, bufsz_p, id, valsz)) == NULL)
1500d63ce2bSvenki 		return (NULL);
1510d63ce2bSvenki 
1520d63ce2bSvenki 	/*
1530d63ce2bSvenki 	 * If we have enough space left, encode the integer
1540d63ce2bSvenki 	 */
1550d63ce2bSvenki 	if (*bufsz_p < valsz)
1560d63ce2bSvenki 		return (NULL);
1570d63ce2bSvenki 	else {
1580d63ce2bSvenki 		valp = (uchar_t *)&ival;
1590d63ce2bSvenki 		for (i = 0; i < valsz; i++)
1600d63ce2bSvenki 			p[i] = valp[sizeof (int) - valsz + i];
1610d63ce2bSvenki 
1620d63ce2bSvenki 		*bufsz_p -= valsz;
1630d63ce2bSvenki 
1640d63ce2bSvenki 		return (p + valsz);
1650d63ce2bSvenki 	}
1660d63ce2bSvenki }
1670d63ce2bSvenki /*
1680d63ce2bSvenki  * Builds an ASN.1 encoded octet string in the buffer. The source string
1690d63ce2bSvenki  * need not be null-terminated.
1700d63ce2bSvenki  */
1710d63ce2bSvenki uchar_t *
asn_build_string(uchar_t * buf,size_t * bufsz_p,uchar_t id,uchar_t * str,size_t slen)1720d63ce2bSvenki asn_build_string(uchar_t *buf, size_t *bufsz_p, uchar_t id, uchar_t *str,
1730d63ce2bSvenki     size_t slen)
1740d63ce2bSvenki {
1750d63ce2bSvenki 	uchar_t	*p;
1760d63ce2bSvenki 
1770d63ce2bSvenki 	if ((p = asn_build_header(buf, bufsz_p, id, slen)) == NULL)
1780d63ce2bSvenki 		return (NULL);
1790d63ce2bSvenki 
1800d63ce2bSvenki 	if (*bufsz_p < slen)
1810d63ce2bSvenki 		return (NULL);
1820d63ce2bSvenki 	else {
183*0409f346SPeter Tribble 		if (str) {
184*0409f346SPeter Tribble 			(void) memcpy(p, str, slen);
185*0409f346SPeter Tribble 		} else {
186*0409f346SPeter Tribble 			(void) memset(p, 0, slen);
187*0409f346SPeter Tribble 		}
1880d63ce2bSvenki 
189*0409f346SPeter Tribble 		*bufsz_p -= slen;
1900d63ce2bSvenki 
191*0409f346SPeter Tribble 		return (p + slen);
1920d63ce2bSvenki 	}
1930d63ce2bSvenki }
1940d63ce2bSvenki 
1950d63ce2bSvenki /*
1960d63ce2bSvenki  * Builds an Object Identifier into the buffer according to the OID
1970d63ce2bSvenki  * packing and encoding rules.
1980d63ce2bSvenki  */
1990d63ce2bSvenki uchar_t *
asn_build_objid(uchar_t * buf,size_t * bufsz_p,uchar_t id,void * oidp,size_t n_subids)2000d63ce2bSvenki asn_build_objid(uchar_t *buf, size_t *bufsz_p, uchar_t id, void *oidp,
2010d63ce2bSvenki     size_t n_subids)
2020d63ce2bSvenki {
2030d63ce2bSvenki 	oid	*objid = oidp;
2040d63ce2bSvenki 	size_t	oid_asnlen;
2050d63ce2bSvenki 	oid	subid, first_subid;
2060d63ce2bSvenki 	uchar_t	subid_len[MAX_SUBIDS_IN_OID];
2070d63ce2bSvenki 	uchar_t	*p;
2080d63ce2bSvenki 	int	i, ndx;
2090d63ce2bSvenki 
2100d63ce2bSvenki 	/*
2110d63ce2bSvenki 	 * Eliminate invalid cases
2120d63ce2bSvenki 	 */
2130d63ce2bSvenki 	if (n_subids < MIN_SUBIDS_IN_OID || n_subids > MAX_SUBIDS_IN_OID)
2140d63ce2bSvenki 		return (NULL);
2150d63ce2bSvenki 	if ((objid[0] > 2) || (objid[0] < 2 && objid[1] >= 40))
2160d63ce2bSvenki 		return (NULL);
2170d63ce2bSvenki 
2180d63ce2bSvenki 	/*
2190d63ce2bSvenki 	 * The BER encoding rule for the ASN.1 Object Identifier states
2200d63ce2bSvenki 	 * that after packing the first two subids into one, each subsequent
2210d63ce2bSvenki 	 * component is considered as the next subid. Each subidentifier is
2220d63ce2bSvenki 	 * then encoded as a non-negative integer using as few 7-bit blocks
2230d63ce2bSvenki 	 * as possible. The blocks are packed in octets with the first bit of
2240d63ce2bSvenki 	 * each octet equal to 1, except for the last octet of each subid.
2250d63ce2bSvenki 	 */
2260d63ce2bSvenki 	oid_asnlen = 0;
2270d63ce2bSvenki 	for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
2280d63ce2bSvenki 		if (i == 0) {
2290d63ce2bSvenki 			/*
2300d63ce2bSvenki 			 * The packing formula for the first two subids
2310d63ce2bSvenki 			 * of an OID is given by Z = (X * 40) + Y
2320d63ce2bSvenki 			 */
2330d63ce2bSvenki 			subid = objid[0] * 40 + objid[1];
2340d63ce2bSvenki 			first_subid = subid;
2350d63ce2bSvenki 			i++;	/* done with both subids 0 and 1 */
2360d63ce2bSvenki 		} else {
2370d63ce2bSvenki 			subid = objid[i];
2380d63ce2bSvenki 		}
2390d63ce2bSvenki 
2400d63ce2bSvenki 		if (subid < (oid) 0x80)
2410d63ce2bSvenki 			subid_len[ndx] = 1;
2420d63ce2bSvenki 		else if (subid < (oid) 0x4000)
2430d63ce2bSvenki 			subid_len[ndx] = 2;
2440d63ce2bSvenki 		else if (subid < (oid) 0x200000)
2450d63ce2bSvenki 			subid_len[ndx] = 3;
2460d63ce2bSvenki 		else if (subid < (oid) 0x10000000)
2470d63ce2bSvenki 			subid_len[ndx] = 4;
2480d63ce2bSvenki 		else {
2490d63ce2bSvenki 			subid_len[ndx] = 5;
2500d63ce2bSvenki 		}
2510d63ce2bSvenki 
2520d63ce2bSvenki 		oid_asnlen += subid_len[ndx];
2530d63ce2bSvenki 	}
2540d63ce2bSvenki 
2550d63ce2bSvenki 	if ((p = asn_build_header(buf, bufsz_p, id, oid_asnlen)) == NULL)
2560d63ce2bSvenki 		return (NULL);
2570d63ce2bSvenki 
2580d63ce2bSvenki 	if (*bufsz_p < oid_asnlen)
2590d63ce2bSvenki 		return (NULL);
2600d63ce2bSvenki 
2610d63ce2bSvenki 	/*
2620d63ce2bSvenki 	 * Store the encoded OID
2630d63ce2bSvenki 	 */
2640d63ce2bSvenki 	for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
2650d63ce2bSvenki 		if (i == 0) {
2660d63ce2bSvenki 			subid = first_subid;
2670d63ce2bSvenki 			i++;
2680d63ce2bSvenki 		} else {
2690d63ce2bSvenki 			subid = objid[i];
2700d63ce2bSvenki 		}
2710d63ce2bSvenki 
2720d63ce2bSvenki 		switch (subid_len[ndx]) {
2730d63ce2bSvenki 		case 1:
2740d63ce2bSvenki 			*p++ = (uchar_t)subid;
2750d63ce2bSvenki 			break;
2760d63ce2bSvenki 
2770d63ce2bSvenki 		case 2:
2780d63ce2bSvenki 			*p++ = (uchar_t)((subid >> 7) | 0x80);
2790d63ce2bSvenki 			*p++ = (uchar_t)(subid & 0x7f);
2800d63ce2bSvenki 			break;
2810d63ce2bSvenki 
2820d63ce2bSvenki 		case 3:
2830d63ce2bSvenki 			*p++ = (uchar_t)((subid >> 14) | 0x80);
2840d63ce2bSvenki 			*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
2850d63ce2bSvenki 			*p++ = (uchar_t)(subid & 0x7f);
2860d63ce2bSvenki 			break;
2870d63ce2bSvenki 
2880d63ce2bSvenki 		case 4:
2890d63ce2bSvenki 			*p++ = (uchar_t)((subid >> 21) | 0x80);
2900d63ce2bSvenki 			*p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
2910d63ce2bSvenki 			*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
2920d63ce2bSvenki 			*p++ = (uchar_t)(subid & 0x7f);
2930d63ce2bSvenki 			break;
2940d63ce2bSvenki 
2950d63ce2bSvenki 		case 5:
2960d63ce2bSvenki 			*p++ = (uchar_t)((subid >> 28) | 0x80);
2970d63ce2bSvenki 			*p++ = (uchar_t)(((subid >> 21) & 0x7f) | 0x80);
2980d63ce2bSvenki 			*p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
2990d63ce2bSvenki 			*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
3000d63ce2bSvenki 			*p++ = (uchar_t)(subid & 0x7f);
3010d63ce2bSvenki 			break;
3020d63ce2bSvenki 		}
3030d63ce2bSvenki 	}
3040d63ce2bSvenki 
3050d63ce2bSvenki 	*bufsz_p -= oid_asnlen;
3060d63ce2bSvenki 
3070d63ce2bSvenki 	return (p);
3080d63ce2bSvenki }
3090d63ce2bSvenki /*
3100d63ce2bSvenki  * Build an ASN_NULL object val into the request packet
3110d63ce2bSvenki  */
3120d63ce2bSvenki uchar_t *
asn_build_null(uchar_t * buf,size_t * bufsz_p,uchar_t id)3130d63ce2bSvenki asn_build_null(uchar_t *buf, size_t *bufsz_p, uchar_t id)
3140d63ce2bSvenki {
3150d63ce2bSvenki 	uchar_t	*p;
3160d63ce2bSvenki 
3170d63ce2bSvenki 	p = asn_build_header(buf, bufsz_p, id, 0);
3180d63ce2bSvenki 
3190d63ce2bSvenki 	return (p);
3200d63ce2bSvenki }
3210d63ce2bSvenki 
3220d63ce2bSvenki 
3230d63ce2bSvenki 
3240d63ce2bSvenki /*
3250d63ce2bSvenki  * This routine parses a 'SEQUENCE OF' object header from the input
3260d63ce2bSvenki  * buffer stream. If the identifier tag (made up of class, constructed
3270d63ce2bSvenki  * type and data type tag) does not match the expected identifier tag,
3280d63ce2bSvenki  * returns failure.
3290d63ce2bSvenki  */
3300d63ce2bSvenki uchar_t *
asn_parse_sequence(uchar_t * buf,size_t * bufsz_p,uchar_t exp_id)3310d63ce2bSvenki asn_parse_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t exp_id)
3320d63ce2bSvenki {
3330d63ce2bSvenki 	uchar_t	*p;
3340d63ce2bSvenki 	uchar_t	id;
3350d63ce2bSvenki 
3360d63ce2bSvenki 	if ((p = asn_parse_header(buf, bufsz_p, &id)) == NULL)
3370d63ce2bSvenki 		return (NULL);
3380d63ce2bSvenki 
3390d63ce2bSvenki 	if (id != exp_id)
3400d63ce2bSvenki 		return (NULL);
3410d63ce2bSvenki 
3420d63ce2bSvenki 	return (p);
3430d63ce2bSvenki }
3440d63ce2bSvenki /*
3450d63ce2bSvenki  * Return the type identifier of the ASN object via 'id'
3460d63ce2bSvenki  */
3470d63ce2bSvenki uchar_t *
asn_parse_header(uchar_t * buf,size_t * bufsz_p,uchar_t * id)3480d63ce2bSvenki asn_parse_header(uchar_t *buf, size_t *bufsz_p, uchar_t *id)
3490d63ce2bSvenki {
3500d63ce2bSvenki 	uchar_t	*p;
3510d63ce2bSvenki 	size_t	asnobj_len, hdrlen;
3520d63ce2bSvenki 
3530d63ce2bSvenki 	/*
3540d63ce2bSvenki 	 * Objects with extension tag type are not supported
3550d63ce2bSvenki 	 */
3560d63ce2bSvenki 	if ((buf[0] & ASN_EXT_TAG) == ASN_EXT_TAG)
3570d63ce2bSvenki 		return (NULL);
3580d63ce2bSvenki 
3590d63ce2bSvenki 	/*
3600d63ce2bSvenki 	 * Parse the length field of the ASN object in the header
3610d63ce2bSvenki 	 */
3620d63ce2bSvenki 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
3630d63ce2bSvenki 		return (NULL);
3640d63ce2bSvenki 
3650d63ce2bSvenki 	/*
3660d63ce2bSvenki 	 * Check if the rest of the msg packet is big enough for the
3670d63ce2bSvenki 	 * full length of the object
3680d63ce2bSvenki 	 */
3690d63ce2bSvenki 	hdrlen = p - buf;
3700d63ce2bSvenki 	if (*bufsz_p < (asnobj_len + hdrlen))
3710d63ce2bSvenki 		return (NULL);
3720d63ce2bSvenki 
3730d63ce2bSvenki 	*id = buf[0];
3740d63ce2bSvenki 	*bufsz_p -= hdrlen;
3750d63ce2bSvenki 
3760d63ce2bSvenki 	return (p);
3770d63ce2bSvenki }
3780d63ce2bSvenki /*
3790d63ce2bSvenki  * This routine parses the length of the object as specified in its
3800d63ce2bSvenki  * header. The 'Indefinite' form of representing length is not supported.
3810d63ce2bSvenki  */
3820d63ce2bSvenki uchar_t *
asn_parse_length(uchar_t * buf,size_t * asnobj_len_p)3830d63ce2bSvenki asn_parse_length(uchar_t *buf, size_t *asnobj_len_p)
3840d63ce2bSvenki {
3850d63ce2bSvenki 	uchar_t	*p;
3860d63ce2bSvenki 	int	n_length_octets;
3870d63ce2bSvenki 
3880d63ce2bSvenki 	/*
3890d63ce2bSvenki 	 * First, check for the short-definite form. Length of
3900d63ce2bSvenki 	 * the object is simply the least significant 7-bits of
3910d63ce2bSvenki 	 * the first byte.
3920d63ce2bSvenki 	 */
3930d63ce2bSvenki 	if ((buf[0] & ASN_LONG_LEN) == 0) {
3940d63ce2bSvenki 		*asnobj_len_p = (size_t)buf[0];
3950d63ce2bSvenki 		return (buf + 1);
3960d63ce2bSvenki 	}
3970d63ce2bSvenki 
3980d63ce2bSvenki 	/*
3990d63ce2bSvenki 	 * Then, eliminate the indefinite form. The ASN_LONG_LEN
4000d63ce2bSvenki 	 * bit of the first byte will be set and the least significant
4010d63ce2bSvenki 	 * 7-bites of that byte will be zeros.
4020d63ce2bSvenki 	 */
4030d63ce2bSvenki 	if (buf[0] == (uchar_t)ASN_LONG_LEN)
4040d63ce2bSvenki 		return (NULL);
4050d63ce2bSvenki 
4060d63ce2bSvenki 	/*
4070d63ce2bSvenki 	 * Then, eliminate the long-definite case when the number of
4080d63ce2bSvenki 	 * follow-up octets is more than what the size var can hold.
4090d63ce2bSvenki 	 */
4100d63ce2bSvenki 	n_length_octets = buf[0] & ~ASN_LONG_LEN;
4110d63ce2bSvenki 	if (n_length_octets > sizeof (*asnobj_len_p))
4120d63ce2bSvenki 		return (NULL);
4130d63ce2bSvenki 
4140d63ce2bSvenki 	/*
4150d63ce2bSvenki 	 * Finally gather the length
4160d63ce2bSvenki 	 */
4170d63ce2bSvenki 	p = buf + 1;
4180d63ce2bSvenki 	*asnobj_len_p = 0;
4190d63ce2bSvenki 	while (n_length_octets--) {
4200d63ce2bSvenki 		*asnobj_len_p <<= 8;
4210d63ce2bSvenki 		*asnobj_len_p |= *p++;
4220d63ce2bSvenki 	}
4230d63ce2bSvenki 
4240d63ce2bSvenki 	return (p);
4250d63ce2bSvenki }
4260d63ce2bSvenki /*
4270d63ce2bSvenki  * Parses an integer out of the input buffer
4280d63ce2bSvenki  */
4290d63ce2bSvenki uchar_t *
asn_parse_int(uchar_t * buf,size_t * bufsz_p,int * ival)4300d63ce2bSvenki asn_parse_int(uchar_t *buf, size_t *bufsz_p, int *ival)
4310d63ce2bSvenki {
4320d63ce2bSvenki 	size_t	asnobj_len, hdrlen;
4330d63ce2bSvenki 	uchar_t	int_id;
4340d63ce2bSvenki 	uchar_t	*p;
4350d63ce2bSvenki 
4360d63ce2bSvenki 	int_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4370d63ce2bSvenki 	if (buf[0] != int_id)
4380d63ce2bSvenki 		return (NULL);
4390d63ce2bSvenki 
4400d63ce2bSvenki 	/*
4410d63ce2bSvenki 	 * Read in the length of the object; Note that integers are
4420d63ce2bSvenki 	 * "packed" when sent from agent to manager and vice-versa,
4430d63ce2bSvenki 	 * so the size of the object could be less than sizeof (int).
4440d63ce2bSvenki 	 */
4450d63ce2bSvenki 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
4460d63ce2bSvenki 		return (NULL);
4470d63ce2bSvenki 
4480d63ce2bSvenki 	/*
4490d63ce2bSvenki 	 * Is there sufficient space left in the packet to read the integer ?
4500d63ce2bSvenki 	 */
4510d63ce2bSvenki 	hdrlen = p - buf;
4520d63ce2bSvenki 	if (*bufsz_p < (hdrlen + asnobj_len))
4530d63ce2bSvenki 		return (NULL);
4540d63ce2bSvenki 
4550d63ce2bSvenki 	/*
4560d63ce2bSvenki 	 * Update space left in the buffer after the integer is read
4570d63ce2bSvenki 	 */
4580d63ce2bSvenki 	*bufsz_p -= (hdrlen + asnobj_len);
4590d63ce2bSvenki 
4600d63ce2bSvenki 	/*
4610d63ce2bSvenki 	 * Read in the integer value
4620d63ce2bSvenki 	 */
4630d63ce2bSvenki 	*ival = (*p & ASN_BIT8) ? -1 : 0;
4640d63ce2bSvenki 	while (asnobj_len--) {
4650d63ce2bSvenki 		*ival <<= 8;
4660d63ce2bSvenki 		*ival |= *p++;
4670d63ce2bSvenki 	}
4680d63ce2bSvenki 
4690d63ce2bSvenki 	return (p);
4700d63ce2bSvenki }
4710d63ce2bSvenki /*
4720d63ce2bSvenki  * Parses an unsigned integer out of the input buffer
4730d63ce2bSvenki  */
4740d63ce2bSvenki uchar_t *
asn_parse_uint(uchar_t * buf,size_t * bufsz_p,uint_t * uival)4750d63ce2bSvenki asn_parse_uint(uchar_t *buf, size_t *bufsz_p, uint_t *uival)
4760d63ce2bSvenki {
4770d63ce2bSvenki 	size_t	asnobj_len, hdrlen;
4780d63ce2bSvenki 	uchar_t	*p;
4790d63ce2bSvenki 
4800d63ce2bSvenki 	if ((buf[0] != ASN_COUNTER) && (buf[0] != ASN_TIMETICKS))
4810d63ce2bSvenki 		return (NULL);
4820d63ce2bSvenki 
4830d63ce2bSvenki 	/*
4840d63ce2bSvenki 	 * Read in the length of the object. Integers are sent the same
4850d63ce2bSvenki 	 * way unsigned integers are sent.  Except that, if the MSB was 1
4860d63ce2bSvenki 	 * in the unsigned int value, a null-byte is attached to the front.
4870d63ce2bSvenki 	 * Otherwise, packing rules are the same as for integer values.
4880d63ce2bSvenki 	 */
4890d63ce2bSvenki 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
4900d63ce2bSvenki 		return (NULL);
4910d63ce2bSvenki 
4920d63ce2bSvenki 	/*
4930d63ce2bSvenki 	 * Is there sufficient space left in the packet to read in the value ?
4940d63ce2bSvenki 	 */
4950d63ce2bSvenki 	hdrlen = p - buf;
4960d63ce2bSvenki 	if (*bufsz_p < (hdrlen + asnobj_len))
4970d63ce2bSvenki 		return (NULL);
4980d63ce2bSvenki 
4990d63ce2bSvenki 	/*
5000d63ce2bSvenki 	 * Update space left in the buffer after the uint is read
5010d63ce2bSvenki 	 */
5020d63ce2bSvenki 	*bufsz_p -= (hdrlen + asnobj_len);
5030d63ce2bSvenki 
5040d63ce2bSvenki 	/*
5050d63ce2bSvenki 	 * Read in the unsigned integer (this should never get
5060d63ce2bSvenki 	 * initialized to ~0 if it was sent right)
5070d63ce2bSvenki 	 */
5080d63ce2bSvenki 	*uival = (*p & ASN_BIT8) ? ~0 : 0;
5090d63ce2bSvenki 	while (asnobj_len--) {
5100d63ce2bSvenki 		*uival <<= 8;
5110d63ce2bSvenki 		*uival |= *p++;
5120d63ce2bSvenki 	}
5130d63ce2bSvenki 
5140d63ce2bSvenki 	return (p);
5150d63ce2bSvenki }
5160d63ce2bSvenki /*
5170d63ce2bSvenki  * Parses a string (ASN_OCTET_STR or ASN_BIT_STR) out of the input buffer.
5180d63ce2bSvenki  * The memory for the string is allocated inside the routine and must be
5190d63ce2bSvenki  * freed by the caller when it is no longer needed. If the string type is
5200d63ce2bSvenki  * ASN_OCTET_STR, the returned string is null-terminated, and the returned
5210d63ce2bSvenki  * length indicates the strlen value. If the string type is ASN_BIT_STR,
5220d63ce2bSvenki  * the returned string is not null-terminated, and the returned length
5230d63ce2bSvenki  * indicates the number of bytes.
5240d63ce2bSvenki  */
5250d63ce2bSvenki uchar_t *
asn_parse_string(uchar_t * buf,size_t * bufsz_p,uchar_t ** str_p,size_t * slen)5260d63ce2bSvenki asn_parse_string(uchar_t *buf, size_t *bufsz_p, uchar_t **str_p, size_t *slen)
5270d63ce2bSvenki {
5280d63ce2bSvenki 	uchar_t	*p;
5290d63ce2bSvenki 	uchar_t	id1, id2;
5300d63ce2bSvenki 	size_t	asnobj_len, hdrlen;
5310d63ce2bSvenki 
5320d63ce2bSvenki 	/*
5330d63ce2bSvenki 	 * Octet and bit strings are supported
5340d63ce2bSvenki 	 */
5350d63ce2bSvenki 	id1 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
5360d63ce2bSvenki 	id2 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR;
5370d63ce2bSvenki 	if ((buf[0] != id1) && (buf[0] != id2))
5380d63ce2bSvenki 		return (NULL);
5390d63ce2bSvenki 
5400d63ce2bSvenki 	/*
5410d63ce2bSvenki 	 * Parse out the length of the object and verify source buf sz
5420d63ce2bSvenki 	 */
5430d63ce2bSvenki 	if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
5440d63ce2bSvenki 		return (NULL);
5450d63ce2bSvenki 
5460d63ce2bSvenki 	hdrlen = p - buf;
5470d63ce2bSvenki 	if (*bufsz_p < (hdrlen + asnobj_len))
5480d63ce2bSvenki 		return (NULL);
5490d63ce2bSvenki 
5500d63ce2bSvenki 	/*
5510d63ce2bSvenki 	 * Allocate for and copy out the string
5520d63ce2bSvenki 	 */
5530d63ce2bSvenki 	if ((*str_p = (uchar_t *)calloc(1, asnobj_len + 1)) == NULL)
5540d63ce2bSvenki 		return (NULL);
5550d63ce2bSvenki 
5560d63ce2bSvenki 	(void) memcpy(*str_p, p, asnobj_len);
5570d63ce2bSvenki 
5580d63ce2bSvenki 	/*
5590d63ce2bSvenki 	 * Terminate the octet string with a null
5600d63ce2bSvenki 	 */
5610d63ce2bSvenki 	if (buf[0] == id1) {
5620d63ce2bSvenki 		(*str_p)[asnobj_len] = 0;
5630d63ce2bSvenki 	}
5640d63ce2bSvenki 
5650d63ce2bSvenki 	/*
5660d63ce2bSvenki 	 * Update pointers and return
5670d63ce2bSvenki 	 */
5680d63ce2bSvenki 	*slen = asnobj_len;
5690d63ce2bSvenki 	*bufsz_p -= (hdrlen + asnobj_len);
5700d63ce2bSvenki 
5710d63ce2bSvenki 	return (p + asnobj_len);
5720d63ce2bSvenki }
5730d63ce2bSvenki /*
5740d63ce2bSvenki  * Parses an object identifier out of the input packet buffer. Space for
5750d63ce2bSvenki  * the oid object is allocated within this routine and must be freed by the
5760d63ce2bSvenki  * caller when no longer needed.
5770d63ce2bSvenki  */
5780d63ce2bSvenki uchar_t *
asn_parse_objid(uchar_t * msg,size_t * varsz_p,void * oidp,size_t * n_subids)5790d63ce2bSvenki asn_parse_objid(uchar_t *msg, size_t *varsz_p, void *oidp, size_t *n_subids)
5800d63ce2bSvenki {
5810d63ce2bSvenki 	oid	**objid_p = oidp;
5820d63ce2bSvenki 	oid	*objid;
5830d63ce2bSvenki 	uchar_t	*p;
5840d63ce2bSvenki 	size_t	hdrlen, asnobj_len;
5850d63ce2bSvenki 	oid	subid;
5860d63ce2bSvenki 	int	i, ndx;
5870d63ce2bSvenki 	uchar_t	exp_id;
5880d63ce2bSvenki 
5890d63ce2bSvenki 	/*
5900d63ce2bSvenki 	 * Check id
5910d63ce2bSvenki 	 */
5920d63ce2bSvenki 	exp_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
5930d63ce2bSvenki 	if (msg[0] != exp_id)
5940d63ce2bSvenki 		return (NULL);
5950d63ce2bSvenki 
5960d63ce2bSvenki 	/*
5970d63ce2bSvenki 	 * Read object length
5980d63ce2bSvenki 	 */
5990d63ce2bSvenki 	if ((p = asn_parse_length(msg + 1, &asnobj_len)) == NULL)
6000d63ce2bSvenki 		return (NULL);
6010d63ce2bSvenki 
6020d63ce2bSvenki 	/*
6030d63ce2bSvenki 	 * Check space in input message
6040d63ce2bSvenki 	 */
6050d63ce2bSvenki 	hdrlen = p - msg;
6060d63ce2bSvenki 	if (*varsz_p < (hdrlen + asnobj_len))
6070d63ce2bSvenki 		return (NULL);
6080d63ce2bSvenki 
6090d63ce2bSvenki 	/*
6100d63ce2bSvenki 	 * Since the OID subidentifiers are packed in 7-bit blocks with
6110d63ce2bSvenki 	 * MSB set to 1 for all but the last octet, the number of subids
6120d63ce2bSvenki 	 * is simply the number of octets with MSB equal to 0, plus 1
6130d63ce2bSvenki 	 * (since the first two subids were packed into one subid and have
6140d63ce2bSvenki 	 * to be expanded back to two).
6150d63ce2bSvenki 	 */
6160d63ce2bSvenki 	*n_subids = 1;
6170d63ce2bSvenki 	for (i = 0; i < asnobj_len; i++) {
6180d63ce2bSvenki 		if ((p[i] & ASN_BIT8) == 0)
6190d63ce2bSvenki 			(*n_subids)++;
6200d63ce2bSvenki 	}
6210d63ce2bSvenki 
6220d63ce2bSvenki 	/*
6230d63ce2bSvenki 	 * Now allocate for the oid and parse the OID into it
6240d63ce2bSvenki 	 */
6250d63ce2bSvenki 	if ((objid = (oid *) calloc(1, (*n_subids) * sizeof (oid))) == NULL)
6260d63ce2bSvenki 		return (NULL);
6270d63ce2bSvenki 
6280d63ce2bSvenki 	ndx = 1;	/* start from 1 to allow for unpacking later */
6290d63ce2bSvenki 	subid = 0;
6300d63ce2bSvenki 	for (i = 0; i < asnobj_len; i++) {
6310d63ce2bSvenki 		subid = subid << 7;
6320d63ce2bSvenki 		subid |= (p[i] & ~ASN_BIT8);
6330d63ce2bSvenki 
6340d63ce2bSvenki 		if ((p[i] & ASN_BIT8) == 0) {
6350d63ce2bSvenki 			objid[ndx] = subid;
6360d63ce2bSvenki 			ndx++;
6370d63ce2bSvenki 			subid = 0;
6380d63ce2bSvenki 		}
6390d63ce2bSvenki 	}
6400d63ce2bSvenki 
6410d63ce2bSvenki 	/*
6420d63ce2bSvenki 	 * Now unpack the first two subids from the subid at index 1.
6430d63ce2bSvenki 	 */
6440d63ce2bSvenki 	if (objid[1] < 40) {
6450d63ce2bSvenki 		objid[0] = 0;
6460d63ce2bSvenki 	} else if (objid[1] < 80) {
6470d63ce2bSvenki 		objid[0] = 1;
6480d63ce2bSvenki 		objid[1] -= 40;
6490d63ce2bSvenki 	} else {
6500d63ce2bSvenki 		objid[0] = 2;
6510d63ce2bSvenki 		objid[1] -= 80;
6520d63ce2bSvenki 	}
6530d63ce2bSvenki 
6540d63ce2bSvenki 	*objid_p = objid;
6550d63ce2bSvenki 	*varsz_p -= (hdrlen + asnobj_len);
6560d63ce2bSvenki 
6570d63ce2bSvenki 	return (msg + hdrlen + asnobj_len);
6580d63ce2bSvenki }
6590d63ce2bSvenki /*
6600d63ce2bSvenki  * Parses the value of an OID object out of the input message buffer.
6610d63ce2bSvenki  * Only type tags less than ASN_EXT_TAG (0x1f) are supported.
6620d63ce2bSvenki  */
6630d63ce2bSvenki uchar_t *
asn_parse_objval(uchar_t * msg,size_t * varsz_p,void * varlistp)6640d63ce2bSvenki asn_parse_objval(uchar_t *msg, size_t *varsz_p, void *varlistp)
6650d63ce2bSvenki {
6660d63ce2bSvenki 	pdu_varlist_t	*vp = varlistp;
6670d63ce2bSvenki 	uchar_t	*p;
6680d63ce2bSvenki 	size_t	n_subids;
6690d63ce2bSvenki 	size_t	hdrlen, asnobj_len;
6700d63ce2bSvenki 
6710d63ce2bSvenki 	vp->type = msg[0] & ASN_EXT_TAG;
6720d63ce2bSvenki 	if (vp->type == ASN_EXT_TAG)
6730d63ce2bSvenki 		return (NULL);
6740d63ce2bSvenki 
6750d63ce2bSvenki 	/*
6760d63ce2bSvenki 	 * Currently we handle ASN_INTEGER, ASN_OCTET_STR, ASN_BIT_STR
6770d63ce2bSvenki 	 * and ASN_TIMETICKS types.
6780d63ce2bSvenki 	 */
6790d63ce2bSvenki 	switch (msg[0]) {
6800d63ce2bSvenki 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER:
6810d63ce2bSvenki 		vp->val.iptr = (int *)calloc(1, sizeof (int));
6820d63ce2bSvenki 		if (vp->val.iptr == NULL)
6830d63ce2bSvenki 			return (NULL);
6840d63ce2bSvenki 
6850d63ce2bSvenki 		if ((p = asn_parse_int(msg, varsz_p, vp->val.iptr)) == NULL) {
6860d63ce2bSvenki 			free(vp->val.iptr);
6870d63ce2bSvenki 			return (NULL);
6880d63ce2bSvenki 		}
6890d63ce2bSvenki 		vp->val_len = sizeof (int);
6900d63ce2bSvenki 		break;
6910d63ce2bSvenki 
6920d63ce2bSvenki 	case ASN_COUNTER:
6930d63ce2bSvenki 	case ASN_TIMETICKS:
6940d63ce2bSvenki 		vp->val.uiptr = (uint_t *)calloc(1, sizeof (uint_t));
6950d63ce2bSvenki 		if (vp->val.uiptr == NULL)
6960d63ce2bSvenki 			return (NULL);
6970d63ce2bSvenki 
6980d63ce2bSvenki 		if ((p = asn_parse_uint(msg, varsz_p, vp->val.uiptr)) == NULL) {
6990d63ce2bSvenki 			free(vp->val.uiptr);
7000d63ce2bSvenki 			return (NULL);
7010d63ce2bSvenki 		}
7020d63ce2bSvenki 		vp->val_len = sizeof (uint_t);
7030d63ce2bSvenki 		vp->type = msg[0];
7040d63ce2bSvenki 		break;
7050d63ce2bSvenki 
7060d63ce2bSvenki 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR:
7070d63ce2bSvenki 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR:
7080d63ce2bSvenki 		p = asn_parse_string(msg, varsz_p, &vp->val.str, &vp->val_len);
7090d63ce2bSvenki 		if (p == NULL)
7100d63ce2bSvenki 			return (NULL);
7110d63ce2bSvenki 		break;
7120d63ce2bSvenki 
7130d63ce2bSvenki 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID:
7140d63ce2bSvenki 		p = asn_parse_objid(msg, varsz_p, &vp->val.objid, &n_subids);
7150d63ce2bSvenki 		if (p == NULL)
7160d63ce2bSvenki 			return (NULL);
7170d63ce2bSvenki 		vp->val_len = n_subids * sizeof (oid);
7180d63ce2bSvenki 		break;
7190d63ce2bSvenki 
7200d63ce2bSvenki 	case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_NULL:
7210d63ce2bSvenki 	case SNMP_NOSUCHOBJECT:
7220d63ce2bSvenki 	case SNMP_NOSUCHINSTANCE:
7230d63ce2bSvenki 	case SNMP_ENDOFMIBVIEW:
7240d63ce2bSvenki 	default:
7250d63ce2bSvenki 		p = asn_parse_length(msg + 1, &asnobj_len);
7260d63ce2bSvenki 		if (p == NULL)
7270d63ce2bSvenki 			return (NULL);
7280d63ce2bSvenki 
7290d63ce2bSvenki 		hdrlen = p - msg;
7300d63ce2bSvenki 		if (*varsz_p < (hdrlen + asnobj_len))
7310d63ce2bSvenki 			return (NULL);
7320d63ce2bSvenki 
7330d63ce2bSvenki 		vp->type = msg[0];
7340d63ce2bSvenki 		vp->val_len = asnobj_len;
7350d63ce2bSvenki 
7360d63ce2bSvenki 		*varsz_p -= (hdrlen + asnobj_len);
7370d63ce2bSvenki 		p += asnobj_len;
7380d63ce2bSvenki 		break;
7390d63ce2bSvenki 	}
7400d63ce2bSvenki 
7410d63ce2bSvenki 	return (p);
7420d63ce2bSvenki }
743