xref: /illumos-gate/usr/src/lib/libkmf/ber_der/common/encode.c (revision 99ebb4ca412cb0a19d77a3899a87c055b9c30fa8)
1*99ebb4caSwyllys /*
2*99ebb4caSwyllys  * CDDL HEADER START
3*99ebb4caSwyllys  *
4*99ebb4caSwyllys  * The contents of this file are subject to the terms of the
5*99ebb4caSwyllys  * Common Development and Distribution License (the "License").
6*99ebb4caSwyllys  * You may not use this file except in compliance with the License.
7*99ebb4caSwyllys  *
8*99ebb4caSwyllys  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*99ebb4caSwyllys  * or http://www.opensolaris.org/os/licensing.
10*99ebb4caSwyllys  * See the License for the specific language governing permissions
11*99ebb4caSwyllys  * and limitations under the License.
12*99ebb4caSwyllys  *
13*99ebb4caSwyllys  * When distributing Covered Code, include this CDDL HEADER in each
14*99ebb4caSwyllys  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*99ebb4caSwyllys  * If applicable, add the following below this CDDL HEADER, with the
16*99ebb4caSwyllys  * fields enclosed by brackets "[]" replaced with your own identifying
17*99ebb4caSwyllys  * information: Portions Copyright [yyyy] [name of copyright owner]
18*99ebb4caSwyllys  *
19*99ebb4caSwyllys  * CDDL HEADER END
20*99ebb4caSwyllys  */
21*99ebb4caSwyllys 
22*99ebb4caSwyllys /*
23*99ebb4caSwyllys  * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
24*99ebb4caSwyllys  *
25*99ebb4caSwyllys  * The contents of this file are subject to the Netscape Public License
26*99ebb4caSwyllys  * Version 1.0 (the "NPL"); you may not use this file except in
27*99ebb4caSwyllys  * compliance with the NPL.  You may obtain a copy of the NPL at
28*99ebb4caSwyllys  * http://www.mozilla.org/NPL/
29*99ebb4caSwyllys  *
30*99ebb4caSwyllys  * Software distributed under the NPL is distributed on an "AS IS" basis,
31*99ebb4caSwyllys  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
32*99ebb4caSwyllys  * for the specific language governing rights and limitations under the
33*99ebb4caSwyllys  * NPL.
34*99ebb4caSwyllys  *
35*99ebb4caSwyllys  * The Initial Developer of this code under the NPL is Netscape
36*99ebb4caSwyllys  * Communications Corporation.  Portions created by Netscape are
37*99ebb4caSwyllys  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
38*99ebb4caSwyllys  * Reserved.
39*99ebb4caSwyllys  */
40*99ebb4caSwyllys 
41*99ebb4caSwyllys /*
42*99ebb4caSwyllys  * Copyright (c) 1990 Regents of the University of Michigan.
43*99ebb4caSwyllys  * All rights reserved.
44*99ebb4caSwyllys  *
45*99ebb4caSwyllys  * Redistribution and use in source and binary forms are permitted
46*99ebb4caSwyllys  * provided that this notice is preserved and that due credit is given
47*99ebb4caSwyllys  * to the University of Michigan at Ann Arbor. The name of the University
48*99ebb4caSwyllys  * may not be used to endorse or promote products derived from this
49*99ebb4caSwyllys  * software without specific prior written permission. This software
50*99ebb4caSwyllys  * is provided ``as is'' without express or implied warranty.
51*99ebb4caSwyllys  */
52*99ebb4caSwyllys 
53*99ebb4caSwyllys /*
54*99ebb4caSwyllys  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
55*99ebb4caSwyllys  * Use is subject to license terms.
56*99ebb4caSwyllys  */
57*99ebb4caSwyllys 
58*99ebb4caSwyllys #pragma ident	"%Z%%M%	%I%	%E% SMI"
59*99ebb4caSwyllys 
60*99ebb4caSwyllys #include <sys/types.h>
61*99ebb4caSwyllys #include <netinet/in.h>
62*99ebb4caSwyllys #include <inttypes.h>
63*99ebb4caSwyllys 
64*99ebb4caSwyllys #include <ber_der.h>
65*99ebb4caSwyllys #include "kmfber_int.h"
66*99ebb4caSwyllys 
67*99ebb4caSwyllys /* the following constants are used in kmfber_calc_lenlen */
68*99ebb4caSwyllys 
69*99ebb4caSwyllys #define	LENMASK1	0xFF
70*99ebb4caSwyllys #define	LENMASK2 	0xFFFF
71*99ebb4caSwyllys #define	LENMASK3	0xFFFFFF
72*99ebb4caSwyllys #define	LENMASK4	0xFFFFFFFF
73*99ebb4caSwyllys #define	_MASK		0x80
74*99ebb4caSwyllys 
75*99ebb4caSwyllys int
76*99ebb4caSwyllys kmfber_calc_taglen(ber_tag_t tag)
77*99ebb4caSwyllys {
78*99ebb4caSwyllys 	int		i;
79*99ebb4caSwyllys 	ber_int_t	mask;
80*99ebb4caSwyllys 
81*99ebb4caSwyllys 	/* find the first non-all-zero byte in the tag */
82*99ebb4caSwyllys 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
83*99ebb4caSwyllys 		mask = (LENMASK3 << (i * 8));
84*99ebb4caSwyllys 		/* not all zero */
85*99ebb4caSwyllys 		if (tag & mask)
86*99ebb4caSwyllys 			break;
87*99ebb4caSwyllys 	}
88*99ebb4caSwyllys 
89*99ebb4caSwyllys 	return (i + 1);
90*99ebb4caSwyllys }
91*99ebb4caSwyllys 
92*99ebb4caSwyllys static int
93*99ebb4caSwyllys ber_put_tag(BerElement	*ber, ber_tag_t tag, int nosos)
94*99ebb4caSwyllys {
95*99ebb4caSwyllys 	ber_int_t	taglen;
96*99ebb4caSwyllys 	ber_tag_t	ntag;
97*99ebb4caSwyllys 
98*99ebb4caSwyllys 	taglen = kmfber_calc_taglen(tag);
99*99ebb4caSwyllys 
100*99ebb4caSwyllys 	ntag = htonl(tag);
101*99ebb4caSwyllys 
102*99ebb4caSwyllys 	return (kmfber_write(ber,
103*99ebb4caSwyllys 		((char *) &ntag) + sizeof (ber_int_t) - taglen,
104*99ebb4caSwyllys 		taglen, nosos));
105*99ebb4caSwyllys }
106*99ebb4caSwyllys 
107*99ebb4caSwyllys int
108*99ebb4caSwyllys kmfber_calc_lenlen(ber_int_t len)
109*99ebb4caSwyllys {
110*99ebb4caSwyllys 	/*
111*99ebb4caSwyllys 	 * short len if it's less than 128 - one byte giving the len,
112*99ebb4caSwyllys 	 * with bit 8 0.
113*99ebb4caSwyllys 	 */
114*99ebb4caSwyllys 
115*99ebb4caSwyllys 	if (len <= 0x7F)
116*99ebb4caSwyllys 		return (1);
117*99ebb4caSwyllys 
118*99ebb4caSwyllys 	/*
119*99ebb4caSwyllys 	 * long len otherwise - one byte with bit 8 set, giving the
120*99ebb4caSwyllys 	 * length of the length, followed by the length itself.
121*99ebb4caSwyllys 	 */
122*99ebb4caSwyllys 
123*99ebb4caSwyllys 	if (len <= LENMASK1)
124*99ebb4caSwyllys 		return (2);
125*99ebb4caSwyllys 	if (len <= LENMASK2)
126*99ebb4caSwyllys 		return (3);
127*99ebb4caSwyllys 	if (len <= LENMASK3)
128*99ebb4caSwyllys 		return (4);
129*99ebb4caSwyllys 
130*99ebb4caSwyllys 	return (5);
131*99ebb4caSwyllys }
132*99ebb4caSwyllys 
133*99ebb4caSwyllys int
134*99ebb4caSwyllys kmfber_put_len(BerElement *ber, ber_int_t len, int nosos)
135*99ebb4caSwyllys {
136*99ebb4caSwyllys 	int		i;
137*99ebb4caSwyllys 	char		lenlen;
138*99ebb4caSwyllys 	ber_int_t	mask, netlen;
139*99ebb4caSwyllys 
140*99ebb4caSwyllys 	/*
141*99ebb4caSwyllys 	 * short len if it's less than 128 - one byte giving the len,
142*99ebb4caSwyllys 	 * with bit 8 0.
143*99ebb4caSwyllys 	 */
144*99ebb4caSwyllys 	if (len <= 127) {
145*99ebb4caSwyllys 		netlen = htonl(len);
146*99ebb4caSwyllys 		return (kmfber_write(ber,
147*99ebb4caSwyllys 			(char *)&netlen + sizeof (ber_int_t) - 1,
148*99ebb4caSwyllys 			1, nosos));
149*99ebb4caSwyllys 	}
150*99ebb4caSwyllys 
151*99ebb4caSwyllys 	/*
152*99ebb4caSwyllys 	 * long len otherwise - one byte with bit 8 set, giving the
153*99ebb4caSwyllys 	 * length of the length, followed by the length itself.
154*99ebb4caSwyllys 	 */
155*99ebb4caSwyllys 
156*99ebb4caSwyllys 	/* find the first non-all-zero byte */
157*99ebb4caSwyllys 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
158*99ebb4caSwyllys 		mask = (LENMASK1 << (i * 8));
159*99ebb4caSwyllys 		/* not all zero */
160*99ebb4caSwyllys 		if (len & mask)
161*99ebb4caSwyllys 			break;
162*99ebb4caSwyllys 	}
163*99ebb4caSwyllys 	lenlen = ++i;
164*99ebb4caSwyllys 	if (lenlen > 4)
165*99ebb4caSwyllys 		return (-1);
166*99ebb4caSwyllys 	lenlen |= 0x80;
167*99ebb4caSwyllys 
168*99ebb4caSwyllys 	/* write the length of the length */
169*99ebb4caSwyllys 	if (kmfber_write(ber, &lenlen, 1, nosos) != 1)
170*99ebb4caSwyllys 		return (-1);
171*99ebb4caSwyllys 
172*99ebb4caSwyllys 	/* write the length itself */
173*99ebb4caSwyllys 	netlen = htonl(len);
174*99ebb4caSwyllys 	if (kmfber_write(ber,
175*99ebb4caSwyllys 		(char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i)
176*99ebb4caSwyllys 		return (-1);
177*99ebb4caSwyllys 
178*99ebb4caSwyllys 	return (i + 1);
179*99ebb4caSwyllys }
180*99ebb4caSwyllys 
181*99ebb4caSwyllys static int
182*99ebb4caSwyllys ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
183*99ebb4caSwyllys {
184*99ebb4caSwyllys 	int		i, sign;
185*99ebb4caSwyllys 	ber_int_t	len, lenlen, taglen, netnum, mask;
186*99ebb4caSwyllys 
187*99ebb4caSwyllys 	sign = (num < 0);
188*99ebb4caSwyllys 
189*99ebb4caSwyllys 	/*
190*99ebb4caSwyllys 	 * high bit is set - look for first non-all-one byte
191*99ebb4caSwyllys 	 * high bit is clear - look for first non-all-zero byte
192*99ebb4caSwyllys 	 */
193*99ebb4caSwyllys 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
194*99ebb4caSwyllys 		mask = (LENMASK1 << (i * 8));
195*99ebb4caSwyllys 
196*99ebb4caSwyllys 		if (sign) {
197*99ebb4caSwyllys 			/* not all ones */
198*99ebb4caSwyllys 			if ((num & mask) != mask)
199*99ebb4caSwyllys 				break;
200*99ebb4caSwyllys 		} else {
201*99ebb4caSwyllys 			/* not all zero */
202*99ebb4caSwyllys 			if (num & mask)
203*99ebb4caSwyllys 				break;
204*99ebb4caSwyllys 		}
205*99ebb4caSwyllys 	}
206*99ebb4caSwyllys 
207*99ebb4caSwyllys 	/*
208*99ebb4caSwyllys 	 * we now have the "leading byte".  if the high bit on this
209*99ebb4caSwyllys 	 * byte matches the sign bit, we need to "back up" a byte.
210*99ebb4caSwyllys 	 */
211*99ebb4caSwyllys 	mask = (num & (_MASK << (i * 8)));
212*99ebb4caSwyllys 	if ((mask && !sign) || (sign && !mask))
213*99ebb4caSwyllys 		i++;
214*99ebb4caSwyllys 
215*99ebb4caSwyllys 	len = i + 1;
216*99ebb4caSwyllys 
217*99ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
218*99ebb4caSwyllys 		return (-1);
219*99ebb4caSwyllys 
220*99ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1)
221*99ebb4caSwyllys 		return (-1);
222*99ebb4caSwyllys 	i++;
223*99ebb4caSwyllys 	netnum = htonl(num);
224*99ebb4caSwyllys 	if (kmfber_write(ber,
225*99ebb4caSwyllys 		(char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i)
226*99ebb4caSwyllys 		/* length of tag + length + contents */
227*99ebb4caSwyllys 		return (taglen + lenlen + i);
228*99ebb4caSwyllys 
229*99ebb4caSwyllys 	return (-1);
230*99ebb4caSwyllys }
231*99ebb4caSwyllys 
232*99ebb4caSwyllys static int
233*99ebb4caSwyllys kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
234*99ebb4caSwyllys {
235*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
236*99ebb4caSwyllys 		tag = BER_ENUMERATED;
237*99ebb4caSwyllys 
238*99ebb4caSwyllys 	return (ber_put_int_or_enum(ber, num, tag));
239*99ebb4caSwyllys }
240*99ebb4caSwyllys 
241*99ebb4caSwyllys int
242*99ebb4caSwyllys ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag)
243*99ebb4caSwyllys {
244*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
245*99ebb4caSwyllys 		tag = BER_INTEGER;
246*99ebb4caSwyllys 
247*99ebb4caSwyllys 	return (ber_put_int_or_enum(ber, num, tag));
248*99ebb4caSwyllys }
249*99ebb4caSwyllys 
250*99ebb4caSwyllys int
251*99ebb4caSwyllys ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag)
252*99ebb4caSwyllys {
253*99ebb4caSwyllys 	ber_int_t taglen, lenlen, rc, len;
254*99ebb4caSwyllys 
255*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
256*99ebb4caSwyllys 		tag = 0x06; 	/* TODO: Add new OID constant to header */
257*99ebb4caSwyllys 
258*99ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
259*99ebb4caSwyllys 		return (-1);
260*99ebb4caSwyllys 
261*99ebb4caSwyllys 	len = (ber_int_t)oid->bv_len;
262*99ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 ||
263*99ebb4caSwyllys 		kmfber_write(ber, oid->bv_val, oid->bv_len, 0) !=
264*99ebb4caSwyllys 		(ber_int_t)oid->bv_len) {
265*99ebb4caSwyllys 		rc = -1;
266*99ebb4caSwyllys 	} else {
267*99ebb4caSwyllys 		/* return length of tag + length + contents */
268*99ebb4caSwyllys 		rc = taglen + lenlen + oid->bv_len;
269*99ebb4caSwyllys 	}
270*99ebb4caSwyllys 	return (rc);
271*99ebb4caSwyllys }
272*99ebb4caSwyllys 
273*99ebb4caSwyllys int
274*99ebb4caSwyllys ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data,
275*99ebb4caSwyllys 	ber_len_t len)
276*99ebb4caSwyllys {
277*99ebb4caSwyllys 	ber_int_t taglen, lenlen, ilen, rc;
278*99ebb4caSwyllys 
279*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
280*99ebb4caSwyllys 		tag = BER_INTEGER;
281*99ebb4caSwyllys 
282*99ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
283*99ebb4caSwyllys 		return (-1);
284*99ebb4caSwyllys 
285*99ebb4caSwyllys 	ilen = (ber_int_t)len;
286*99ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
287*99ebb4caSwyllys 		kmfber_write(ber, data, len, 0) != (ber_int_t)len) {
288*99ebb4caSwyllys 		rc = -1;
289*99ebb4caSwyllys 	} else {
290*99ebb4caSwyllys 		/* return length of tag + length + contents */
291*99ebb4caSwyllys 		rc = taglen + lenlen + len;
292*99ebb4caSwyllys 	}
293*99ebb4caSwyllys 	return (rc);
294*99ebb4caSwyllys }
295*99ebb4caSwyllys 
296*99ebb4caSwyllys static int
297*99ebb4caSwyllys kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len,
298*99ebb4caSwyllys 	ber_tag_t tag)
299*99ebb4caSwyllys {
300*99ebb4caSwyllys 	ber_int_t	taglen, lenlen, ilen, rc;
301*99ebb4caSwyllys #ifdef STR_TRANSLATION
302*99ebb4caSwyllys 	int	free_str;
303*99ebb4caSwyllys #endif /* STR_TRANSLATION */
304*99ebb4caSwyllys 
305*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
306*99ebb4caSwyllys 		tag = BER_OCTET_STRING;
307*99ebb4caSwyllys 
308*99ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
309*99ebb4caSwyllys 		return (-1);
310*99ebb4caSwyllys 
311*99ebb4caSwyllys #ifdef STR_TRANSLATION
312*99ebb4caSwyllys 	if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 &&
313*99ebb4caSwyllys 	    ber->ber_encode_translate_proc != NULL) {
314*99ebb4caSwyllys 		if ((*(ber->ber_encode_translate_proc))(&str, &len, 0)
315*99ebb4caSwyllys 		    != 0) {
316*99ebb4caSwyllys 			return (-1);
317*99ebb4caSwyllys 		}
318*99ebb4caSwyllys 		free_str = 1;
319*99ebb4caSwyllys 	} else {
320*99ebb4caSwyllys 		free_str = 0;
321*99ebb4caSwyllys 	}
322*99ebb4caSwyllys #endif /* STR_TRANSLATION */
323*99ebb4caSwyllys 
324*99ebb4caSwyllys 	/*
325*99ebb4caSwyllys 	 *  Note:  below is a spot where we limit ber_write
326*99ebb4caSwyllys 	 *	to signed long (instead of unsigned long)
327*99ebb4caSwyllys 	 */
328*99ebb4caSwyllys 	ilen = (ber_int_t)len;
329*99ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
330*99ebb4caSwyllys 		kmfber_write(ber, str, len, 0) != (ber_int_t)len) {
331*99ebb4caSwyllys 		rc = -1;
332*99ebb4caSwyllys 	} else {
333*99ebb4caSwyllys 		/* return length of tag + length + contents */
334*99ebb4caSwyllys 		rc = taglen + lenlen + len;
335*99ebb4caSwyllys 	}
336*99ebb4caSwyllys 
337*99ebb4caSwyllys #ifdef STR_TRANSLATION
338*99ebb4caSwyllys 	if (free_str) {
339*99ebb4caSwyllys 		free(str);
340*99ebb4caSwyllys 	}
341*99ebb4caSwyllys #endif /* STR_TRANSLATION */
342*99ebb4caSwyllys 
343*99ebb4caSwyllys 	return (rc);
344*99ebb4caSwyllys }
345*99ebb4caSwyllys 
346*99ebb4caSwyllys static int
347*99ebb4caSwyllys kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag)
348*99ebb4caSwyllys {
349*99ebb4caSwyllys 	return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag));
350*99ebb4caSwyllys }
351*99ebb4caSwyllys 
352*99ebb4caSwyllys static int
353*99ebb4caSwyllys kmfber_put_bitstring(BerElement *ber, char *str,
354*99ebb4caSwyllys 	ber_len_t blen /* in bits */, ber_tag_t tag)
355*99ebb4caSwyllys {
356*99ebb4caSwyllys 	ber_int_t	taglen, lenlen, len;
357*99ebb4caSwyllys 	unsigned char	unusedbits;
358*99ebb4caSwyllys 
359*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
360*99ebb4caSwyllys 		tag = BER_BIT_STRING;
361*99ebb4caSwyllys 
362*99ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
363*99ebb4caSwyllys 		return (-1);
364*99ebb4caSwyllys 
365*99ebb4caSwyllys 	len = (blen + 7) / 8;
366*99ebb4caSwyllys 	unusedbits = (unsigned char) (len * 8 - blen);
367*99ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1)
368*99ebb4caSwyllys 		return (-1);
369*99ebb4caSwyllys 
370*99ebb4caSwyllys 	if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1)
371*99ebb4caSwyllys 		return (-1);
372*99ebb4caSwyllys 
373*99ebb4caSwyllys 	if (kmfber_write(ber, str, len, 0) != len)
374*99ebb4caSwyllys 		return (-1);
375*99ebb4caSwyllys 
376*99ebb4caSwyllys 	/* return length of tag + length + unused bit count + contents */
377*99ebb4caSwyllys 	return (taglen + 1 + lenlen + len);
378*99ebb4caSwyllys }
379*99ebb4caSwyllys 
380*99ebb4caSwyllys static int
381*99ebb4caSwyllys kmfber_put_null(BerElement *ber, ber_tag_t tag)
382*99ebb4caSwyllys {
383*99ebb4caSwyllys 	int	taglen;
384*99ebb4caSwyllys 
385*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
386*99ebb4caSwyllys 		tag = BER_NULL;
387*99ebb4caSwyllys 
388*99ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
389*99ebb4caSwyllys 		return (-1);
390*99ebb4caSwyllys 
391*99ebb4caSwyllys 	if (kmfber_put_len(ber, 0, 0) != 1)
392*99ebb4caSwyllys 		return (-1);
393*99ebb4caSwyllys 
394*99ebb4caSwyllys 	return (taglen + 1);
395*99ebb4caSwyllys }
396*99ebb4caSwyllys 
397*99ebb4caSwyllys static int
398*99ebb4caSwyllys kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag)
399*99ebb4caSwyllys {
400*99ebb4caSwyllys 	int		taglen;
401*99ebb4caSwyllys 	unsigned char	trueval = 0xff;
402*99ebb4caSwyllys 	unsigned char	falseval = 0x00;
403*99ebb4caSwyllys 
404*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
405*99ebb4caSwyllys 		tag = BER_BOOLEAN;
406*99ebb4caSwyllys 
407*99ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
408*99ebb4caSwyllys 		return (-1);
409*99ebb4caSwyllys 
410*99ebb4caSwyllys 	if (kmfber_put_len(ber, 1, 0) != 1)
411*99ebb4caSwyllys 		return (-1);
412*99ebb4caSwyllys 
413*99ebb4caSwyllys 	if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0)
414*99ebb4caSwyllys 	    != 1)
415*99ebb4caSwyllys 		return (-1);
416*99ebb4caSwyllys 
417*99ebb4caSwyllys 	return (taglen + 2);
418*99ebb4caSwyllys }
419*99ebb4caSwyllys 
420*99ebb4caSwyllys #define	FOUR_BYTE_LEN	5
421*99ebb4caSwyllys 
422*99ebb4caSwyllys 
423*99ebb4caSwyllys /*
424*99ebb4caSwyllys  * The idea here is roughly this: we maintain a stack of these Seqorset
425*99ebb4caSwyllys  * structures. This is pushed when we see the beginning of a new set or
426*99ebb4caSwyllys  * sequence. It is popped when we see the end of a set or sequence.
427*99ebb4caSwyllys  * Since we don't want to malloc and free these structures all the time,
428*99ebb4caSwyllys  * we pre-allocate a small set of them within the ber element structure.
429*99ebb4caSwyllys  * thus we need to spot when we've overflowed this stack and fall back to
430*99ebb4caSwyllys  * malloc'ing instead.
431*99ebb4caSwyllys  */
432*99ebb4caSwyllys static int
433*99ebb4caSwyllys ber_start_seqorset(BerElement *ber, ber_tag_t tag)
434*99ebb4caSwyllys {
435*99ebb4caSwyllys 	Seqorset	*new_sos;
436*99ebb4caSwyllys 
437*99ebb4caSwyllys 	/* can we fit into the local stack ? */
438*99ebb4caSwyllys 	if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
439*99ebb4caSwyllys 		/* yes */
440*99ebb4caSwyllys 		new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
441*99ebb4caSwyllys 	} else {
442*99ebb4caSwyllys 		/* no */
443*99ebb4caSwyllys 		if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset)))
444*99ebb4caSwyllys 		    == NULLSEQORSET) {
445*99ebb4caSwyllys 			return (-1);
446*99ebb4caSwyllys 		}
447*99ebb4caSwyllys 	}
448*99ebb4caSwyllys 	ber->ber_sos_stack_posn++;
449*99ebb4caSwyllys 
450*99ebb4caSwyllys 	if (ber->ber_sos == NULLSEQORSET)
451*99ebb4caSwyllys 		new_sos->sos_first = ber->ber_ptr;
452*99ebb4caSwyllys 	else
453*99ebb4caSwyllys 		new_sos->sos_first = ber->ber_sos->sos_ptr;
454*99ebb4caSwyllys 
455*99ebb4caSwyllys 	/* Set aside room for a 4 byte length field */
456*99ebb4caSwyllys 	new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) +
457*99ebb4caSwyllys 		FOUR_BYTE_LEN;
458*99ebb4caSwyllys 	new_sos->sos_tag = tag;
459*99ebb4caSwyllys 
460*99ebb4caSwyllys 	new_sos->sos_next = ber->ber_sos;
461*99ebb4caSwyllys 	new_sos->sos_clen = 0;
462*99ebb4caSwyllys 
463*99ebb4caSwyllys 	ber->ber_sos = new_sos;
464*99ebb4caSwyllys 	if (ber->ber_sos->sos_ptr > ber->ber_end) {
465*99ebb4caSwyllys 		(void) realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end);
466*99ebb4caSwyllys 	}
467*99ebb4caSwyllys 	return (0);
468*99ebb4caSwyllys }
469*99ebb4caSwyllys 
470*99ebb4caSwyllys static int
471*99ebb4caSwyllys kmfber_start_seq(BerElement *ber, ber_tag_t tag)
472*99ebb4caSwyllys {
473*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
474*99ebb4caSwyllys 		tag = BER_CONSTRUCTED_SEQUENCE;
475*99ebb4caSwyllys 
476*99ebb4caSwyllys 	return (ber_start_seqorset(ber, tag));
477*99ebb4caSwyllys }
478*99ebb4caSwyllys 
479*99ebb4caSwyllys static int
480*99ebb4caSwyllys kmfber_start_set(BerElement *ber, ber_tag_t tag)
481*99ebb4caSwyllys {
482*99ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
483*99ebb4caSwyllys 		tag = BER_CONSTRUCTED_SET;
484*99ebb4caSwyllys 
485*99ebb4caSwyllys 	return (ber_start_seqorset(ber, tag));
486*99ebb4caSwyllys }
487*99ebb4caSwyllys 
488*99ebb4caSwyllys static int
489*99ebb4caSwyllys ber_put_seqorset(BerElement *ber)
490*99ebb4caSwyllys {
491*99ebb4caSwyllys 	ber_int_t	netlen, len, taglen, lenlen;
492*99ebb4caSwyllys 	unsigned char	ltag = 0x80 + FOUR_BYTE_LEN - 1;
493*99ebb4caSwyllys 	Seqorset	*next;
494*99ebb4caSwyllys 	Seqorset	**sos = &ber->ber_sos;
495*99ebb4caSwyllys 
496*99ebb4caSwyllys 	/*
497*99ebb4caSwyllys 	 * If this is the toplevel sequence or set, we need to actually
498*99ebb4caSwyllys 	 * write the stuff out.  Otherwise, it's already been put in
499*99ebb4caSwyllys 	 * the appropriate buffer and will be written when the toplevel
500*99ebb4caSwyllys 	 * one is written.  In this case all we need to do is update the
501*99ebb4caSwyllys 	 * length and tag.
502*99ebb4caSwyllys 	 */
503*99ebb4caSwyllys 
504*99ebb4caSwyllys 	len = (*sos)->sos_clen;
505*99ebb4caSwyllys 	netlen = (ber_len_t)htonl(len);
506*99ebb4caSwyllys 
507*99ebb4caSwyllys 	if (ber->ber_options & KMFBER_OPT_USE_DER) {
508*99ebb4caSwyllys 		lenlen = kmfber_calc_lenlen(len);
509*99ebb4caSwyllys 	} else {
510*99ebb4caSwyllys 		lenlen = FOUR_BYTE_LEN;
511*99ebb4caSwyllys 	}
512*99ebb4caSwyllys 
513*99ebb4caSwyllys 	if ((next = (*sos)->sos_next) == NULLSEQORSET) {
514*99ebb4caSwyllys 		/* write the tag */
515*99ebb4caSwyllys 		if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1)
516*99ebb4caSwyllys 			return (-1);
517*99ebb4caSwyllys 
518*99ebb4caSwyllys 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
519*99ebb4caSwyllys 			/* Write the length in the minimum # of octets */
520*99ebb4caSwyllys 			if (kmfber_put_len(ber, len, 1) == -1)
521*99ebb4caSwyllys 				return (-1);
522*99ebb4caSwyllys 
523*99ebb4caSwyllys 			if (lenlen != FOUR_BYTE_LEN) {
524*99ebb4caSwyllys 				/*
525*99ebb4caSwyllys 				 * We set aside FOUR_BYTE_LEN bytes for
526*99ebb4caSwyllys 				 * the length field.  Move the data if
527*99ebb4caSwyllys 				 * we don't actually need that much
528*99ebb4caSwyllys 				 */
529*99ebb4caSwyllys 				(void) memmove((*sos)->sos_first + taglen +
530*99ebb4caSwyllys 				    lenlen, (*sos)->sos_first + taglen +
531*99ebb4caSwyllys 				    FOUR_BYTE_LEN, len);
532*99ebb4caSwyllys 			}
533*99ebb4caSwyllys 		} else {
534*99ebb4caSwyllys 			/* Fill FOUR_BYTE_LEN bytes for length field */
535*99ebb4caSwyllys 			/* one byte of length length */
536*99ebb4caSwyllys 			if (kmfber_write(ber, (char *)&ltag, 1, 1) != 1)
537*99ebb4caSwyllys 				return (-1);
538*99ebb4caSwyllys 
539*99ebb4caSwyllys 			/* the length itself */
540*99ebb4caSwyllys 			if (kmfber_write(ber,
541*99ebb4caSwyllys 				(char *)&netlen + sizeof (ber_int_t)
542*99ebb4caSwyllys 				- (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) !=
543*99ebb4caSwyllys 				FOUR_BYTE_LEN - 1)
544*99ebb4caSwyllys 				return (-1);
545*99ebb4caSwyllys 		}
546*99ebb4caSwyllys 		/* The ber_ptr is at the set/seq start - move it to the end */
547*99ebb4caSwyllys 		ber->ber_ptr += len;
548*99ebb4caSwyllys 	} else {
549*99ebb4caSwyllys 		ber_tag_t	ntag;
550*99ebb4caSwyllys 
551*99ebb4caSwyllys 		/* the tag */
552*99ebb4caSwyllys 		taglen = kmfber_calc_taglen((*sos)->sos_tag);
553*99ebb4caSwyllys 		ntag = htonl((*sos)->sos_tag);
554*99ebb4caSwyllys 		(void) memmove((*sos)->sos_first, (char *)&ntag +
555*99ebb4caSwyllys 		    sizeof (ber_int_t) - taglen, taglen);
556*99ebb4caSwyllys 
557*99ebb4caSwyllys 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
558*99ebb4caSwyllys 			ltag = (lenlen == 1) ? (unsigned char)len :
559*99ebb4caSwyllys 				(unsigned char) (0x80 + (lenlen - 1));
560*99ebb4caSwyllys 		}
561*99ebb4caSwyllys 
562*99ebb4caSwyllys 		/* one byte of length length */
563*99ebb4caSwyllys 		(void) memmove((*sos)->sos_first + 1, &ltag, 1);
564*99ebb4caSwyllys 
565*99ebb4caSwyllys 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
566*99ebb4caSwyllys 			if (lenlen > 1) {
567*99ebb4caSwyllys 				/* Write the length itself */
568*99ebb4caSwyllys 				(void) memmove((*sos)->sos_first + 2,
569*99ebb4caSwyllys 				    (char *)&netlen + sizeof (ber_uint_t) -
570*99ebb4caSwyllys 				    (lenlen - 1),
571*99ebb4caSwyllys 				    lenlen - 1);
572*99ebb4caSwyllys 			}
573*99ebb4caSwyllys 			if (lenlen != FOUR_BYTE_LEN) {
574*99ebb4caSwyllys 				/*
575*99ebb4caSwyllys 				 * We set aside FOUR_BYTE_LEN bytes for
576*99ebb4caSwyllys 				 * the length field.  Move the data if
577*99ebb4caSwyllys 				 * we don't actually need that much
578*99ebb4caSwyllys 				 */
579*99ebb4caSwyllys 				(void) memmove((*sos)->sos_first + taglen +
580*99ebb4caSwyllys 				    lenlen, (*sos)->sos_first + taglen +
581*99ebb4caSwyllys 				    FOUR_BYTE_LEN, len);
582*99ebb4caSwyllys 			}
583*99ebb4caSwyllys 		} else {
584*99ebb4caSwyllys 			/* the length itself */
585*99ebb4caSwyllys 			(void) memmove((*sos)->sos_first + taglen + 1,
586*99ebb4caSwyllys 			    (char *) &netlen + sizeof (ber_int_t) -
587*99ebb4caSwyllys 			    (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1);
588*99ebb4caSwyllys 		}
589*99ebb4caSwyllys 
590*99ebb4caSwyllys 		next->sos_clen += (taglen + lenlen + len);
591*99ebb4caSwyllys 		next->sos_ptr += (taglen + lenlen + len);
592*99ebb4caSwyllys 	}
593*99ebb4caSwyllys 
594*99ebb4caSwyllys 	/* we're done with this seqorset, so free it up */
595*99ebb4caSwyllys 	/* was this one from the local stack ? */
596*99ebb4caSwyllys 	if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) {
597*99ebb4caSwyllys 		free((char *)(*sos));
598*99ebb4caSwyllys 	}
599*99ebb4caSwyllys 	ber->ber_sos_stack_posn--;
600*99ebb4caSwyllys 	*sos = next;
601*99ebb4caSwyllys 
602*99ebb4caSwyllys 	return (taglen + lenlen + len);
603*99ebb4caSwyllys }
604*99ebb4caSwyllys 
605*99ebb4caSwyllys /* VARARGS */
606*99ebb4caSwyllys int
607*99ebb4caSwyllys kmfber_printf(BerElement *ber, const char *fmt, ...)
608*99ebb4caSwyllys {
609*99ebb4caSwyllys 	va_list		ap;
610*99ebb4caSwyllys 	char		*s, **ss;
611*99ebb4caSwyllys 	struct berval	**bv, *oid;
612*99ebb4caSwyllys 	int		rc, i, t;
613*99ebb4caSwyllys 	ber_int_t	len;
614*99ebb4caSwyllys 
615*99ebb4caSwyllys 	va_start(ap, fmt);
616*99ebb4caSwyllys 
617*99ebb4caSwyllys #ifdef KMFBER_DEBUG
618*99ebb4caSwyllys 	if (lber_debug & 64) {
619*99ebb4caSwyllys 		char msg[80];
620*99ebb4caSwyllys 		sprintf(msg, "kmfber_printf fmt (%s)\n", fmt);
621*99ebb4caSwyllys 		ber_err_print(msg);
622*99ebb4caSwyllys 	}
623*99ebb4caSwyllys #endif
624*99ebb4caSwyllys 
625*99ebb4caSwyllys 	for (rc = 0; *fmt && rc != -1; fmt++) {
626*99ebb4caSwyllys 		switch (*fmt) {
627*99ebb4caSwyllys 		case 'b':	/* boolean */
628*99ebb4caSwyllys 			i = va_arg(ap, int);
629*99ebb4caSwyllys 			rc = kmfber_put_boolean(ber, i, ber->ber_tag);
630*99ebb4caSwyllys 			break;
631*99ebb4caSwyllys 
632*99ebb4caSwyllys 		case 'i':	/* int */
633*99ebb4caSwyllys 			i = va_arg(ap, int);
634*99ebb4caSwyllys 			rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag);
635*99ebb4caSwyllys 			break;
636*99ebb4caSwyllys 
637*99ebb4caSwyllys 		case 'D':	/* Object ID */
638*99ebb4caSwyllys 			if ((oid = va_arg(ap, struct berval *)) == NULL)
639*99ebb4caSwyllys 				break;
640*99ebb4caSwyllys 			rc = ber_put_oid(ber, oid, ber->ber_tag);
641*99ebb4caSwyllys 			break;
642*99ebb4caSwyllys 		case 'I':	/* int */
643*99ebb4caSwyllys 			s = va_arg(ap, char *);
644*99ebb4caSwyllys 			len = va_arg(ap, ber_int_t);
645*99ebb4caSwyllys 			rc = ber_put_big_int(ber, ber->ber_tag, s, len);
646*99ebb4caSwyllys 			break;
647*99ebb4caSwyllys 
648*99ebb4caSwyllys 		case 'e':	/* enumeration */
649*99ebb4caSwyllys 			i = va_arg(ap, int);
650*99ebb4caSwyllys 			rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag);
651*99ebb4caSwyllys 			break;
652*99ebb4caSwyllys 
653*99ebb4caSwyllys 		case 'l':
654*99ebb4caSwyllys 			t = va_arg(ap, int);
655*99ebb4caSwyllys 			rc = kmfber_put_len(ber, t, 0);
656*99ebb4caSwyllys 			break;
657*99ebb4caSwyllys 		case 'n':	/* null */
658*99ebb4caSwyllys 			rc = kmfber_put_null(ber, ber->ber_tag);
659*99ebb4caSwyllys 			break;
660*99ebb4caSwyllys 
661*99ebb4caSwyllys 		case 'o':	/* octet string (non-null terminated) */
662*99ebb4caSwyllys 			s = va_arg(ap, char *);
663*99ebb4caSwyllys 			len = va_arg(ap, int);
664*99ebb4caSwyllys 			rc = kmfber_put_ostring(ber, s, len, ber->ber_tag);
665*99ebb4caSwyllys 			break;
666*99ebb4caSwyllys 
667*99ebb4caSwyllys 		case 's':	/* string */
668*99ebb4caSwyllys 			s = va_arg(ap, char *);
669*99ebb4caSwyllys 			rc = kmfber_put_string(ber, s, ber->ber_tag);
670*99ebb4caSwyllys 			break;
671*99ebb4caSwyllys 
672*99ebb4caSwyllys 		case 'B':	/* bit string */
673*99ebb4caSwyllys 			s = va_arg(ap, char *);
674*99ebb4caSwyllys 			len = va_arg(ap, int);	/* in bits */
675*99ebb4caSwyllys 			rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag);
676*99ebb4caSwyllys 			break;
677*99ebb4caSwyllys 
678*99ebb4caSwyllys 		case 't':	/* tag for the next element */
679*99ebb4caSwyllys 			ber->ber_tag = va_arg(ap, ber_tag_t);
680*99ebb4caSwyllys 			ber->ber_usertag = 1;
681*99ebb4caSwyllys 			break;
682*99ebb4caSwyllys 
683*99ebb4caSwyllys 		case 'T': /* Write an explicit tag, but don't change current */
684*99ebb4caSwyllys 			t = va_arg(ap, int);
685*99ebb4caSwyllys 			rc = ber_put_tag(ber, t, 0);
686*99ebb4caSwyllys 			break;
687*99ebb4caSwyllys 
688*99ebb4caSwyllys 		case 'v':	/* vector of strings */
689*99ebb4caSwyllys 			if ((ss = va_arg(ap, char **)) == NULL)
690*99ebb4caSwyllys 				break;
691*99ebb4caSwyllys 			for (i = 0; ss[i] != NULL; i++) {
692*99ebb4caSwyllys 				if ((rc = kmfber_put_string(ber, ss[i],
693*99ebb4caSwyllys 				    ber->ber_tag)) == -1)
694*99ebb4caSwyllys 					break;
695*99ebb4caSwyllys 			}
696*99ebb4caSwyllys 			break;
697*99ebb4caSwyllys 
698*99ebb4caSwyllys 		case 'V':	/* sequences of strings + lengths */
699*99ebb4caSwyllys 			if ((bv = va_arg(ap, struct berval **)) == NULL)
700*99ebb4caSwyllys 				break;
701*99ebb4caSwyllys 			for (i = 0; bv[i] != NULL; i++) {
702*99ebb4caSwyllys 				if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val,
703*99ebb4caSwyllys 				    bv[i]->bv_len, ber->ber_tag)) == -1)
704*99ebb4caSwyllys 					break;
705*99ebb4caSwyllys 			}
706*99ebb4caSwyllys 			break;
707*99ebb4caSwyllys 
708*99ebb4caSwyllys 		case '{':	/* begin sequence */
709*99ebb4caSwyllys 			rc = kmfber_start_seq(ber, ber->ber_tag);
710*99ebb4caSwyllys 			break;
711*99ebb4caSwyllys 
712*99ebb4caSwyllys 		case '}':	/* end sequence */
713*99ebb4caSwyllys 			rc = ber_put_seqorset(ber);
714*99ebb4caSwyllys 			break;
715*99ebb4caSwyllys 
716*99ebb4caSwyllys 		case '[':	/* begin set */
717*99ebb4caSwyllys 			rc = kmfber_start_set(ber, ber->ber_tag);
718*99ebb4caSwyllys 			break;
719*99ebb4caSwyllys 
720*99ebb4caSwyllys 		case ']':	/* end set */
721*99ebb4caSwyllys 			rc = ber_put_seqorset(ber);
722*99ebb4caSwyllys 			break;
723*99ebb4caSwyllys 
724*99ebb4caSwyllys 		default: {
725*99ebb4caSwyllys #ifdef KMFBER_DEBUG
726*99ebb4caSwyllys 				char msg[80];
727*99ebb4caSwyllys 				sprintf(msg, "unknown fmt %c\n", *fmt);
728*99ebb4caSwyllys 				ber_err_print(msg);
729*99ebb4caSwyllys #endif
730*99ebb4caSwyllys 				rc = -1;
731*99ebb4caSwyllys 				break;
732*99ebb4caSwyllys 			}
733*99ebb4caSwyllys 		}
734*99ebb4caSwyllys 
735*99ebb4caSwyllys 		if (ber->ber_usertag == 0)
736*99ebb4caSwyllys 			ber->ber_tag = KMFBER_DEFAULT;
737*99ebb4caSwyllys 		else
738*99ebb4caSwyllys 			ber->ber_usertag = 0;
739*99ebb4caSwyllys 	}
740*99ebb4caSwyllys 
741*99ebb4caSwyllys 	va_end(ap);
742*99ebb4caSwyllys 
743*99ebb4caSwyllys 	return (rc);
744*99ebb4caSwyllys }
745