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