199ebb4caSwyllys /*
299ebb4caSwyllys  * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
399ebb4caSwyllys  *
499ebb4caSwyllys  * The contents of this file are subject to the Netscape Public License
599ebb4caSwyllys  * Version 1.0 (the "NPL"); you may not use this file except in
699ebb4caSwyllys  * compliance with the NPL.  You may obtain a copy of the NPL at
799ebb4caSwyllys  * http://www.mozilla.org/NPL/
899ebb4caSwyllys  *
999ebb4caSwyllys  * Software distributed under the NPL is distributed on an "AS IS" basis,
1099ebb4caSwyllys  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
1199ebb4caSwyllys  * for the specific language governing rights and limitations under the
1299ebb4caSwyllys  * NPL.
1399ebb4caSwyllys  *
1499ebb4caSwyllys  * The Initial Developer of this code under the NPL is Netscape
1599ebb4caSwyllys  * Communications Corporation.  Portions created by Netscape are
1699ebb4caSwyllys  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
1799ebb4caSwyllys  * Reserved.
1899ebb4caSwyllys  */
1999ebb4caSwyllys 
2099ebb4caSwyllys /*
2199ebb4caSwyllys  * Copyright (c) 1990 Regents of the University of Michigan.
2299ebb4caSwyllys  * All rights reserved.
2399ebb4caSwyllys  *
2499ebb4caSwyllys  * Redistribution and use in source and binary forms are permitted
2599ebb4caSwyllys  * provided that this notice is preserved and that due credit is given
2699ebb4caSwyllys  * to the University of Michigan at Ann Arbor. The name of the University
2799ebb4caSwyllys  * may not be used to endorse or promote products derived from this
2899ebb4caSwyllys  * software without specific prior written permission. This software
2999ebb4caSwyllys  * is provided ``as is'' without express or implied warranty.
3099ebb4caSwyllys  */
3199ebb4caSwyllys 
3299ebb4caSwyllys /*
33*56664548SWyllys Ingersoll  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3499ebb4caSwyllys  * Use is subject to license terms.
3599ebb4caSwyllys  */
3699ebb4caSwyllys 
3799ebb4caSwyllys #include <sys/types.h>
3899ebb4caSwyllys #include <netinet/in.h>
3999ebb4caSwyllys #include <inttypes.h>
4099ebb4caSwyllys 
4199ebb4caSwyllys #include <ber_der.h>
4299ebb4caSwyllys #include "kmfber_int.h"
4399ebb4caSwyllys 
4499ebb4caSwyllys /* the following constants are used in kmfber_calc_lenlen */
4599ebb4caSwyllys 
4699ebb4caSwyllys #define	LENMASK1	0xFF
4799ebb4caSwyllys #define	LENMASK2 	0xFFFF
4899ebb4caSwyllys #define	LENMASK3	0xFFFFFF
4999ebb4caSwyllys #define	LENMASK4	0xFFFFFFFF
5099ebb4caSwyllys #define	_MASK		0x80
5199ebb4caSwyllys 
5299ebb4caSwyllys int
kmfber_calc_taglen(ber_tag_t tag)5399ebb4caSwyllys kmfber_calc_taglen(ber_tag_t tag)
5499ebb4caSwyllys {
5599ebb4caSwyllys 	int		i;
5699ebb4caSwyllys 	ber_int_t	mask;
5799ebb4caSwyllys 
5899ebb4caSwyllys 	/* find the first non-all-zero byte in the tag */
5999ebb4caSwyllys 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
6099ebb4caSwyllys 		mask = (LENMASK3 << (i * 8));
6199ebb4caSwyllys 		/* not all zero */
6299ebb4caSwyllys 		if (tag & mask)
6399ebb4caSwyllys 			break;
6499ebb4caSwyllys 	}
6599ebb4caSwyllys 
6699ebb4caSwyllys 	return (i + 1);
6799ebb4caSwyllys }
6899ebb4caSwyllys 
6999ebb4caSwyllys static int
ber_put_tag(BerElement * ber,ber_tag_t tag,int nosos)7099ebb4caSwyllys ber_put_tag(BerElement	*ber, ber_tag_t tag, int nosos)
7199ebb4caSwyllys {
7299ebb4caSwyllys 	ber_int_t	taglen;
7399ebb4caSwyllys 	ber_tag_t	ntag;
7499ebb4caSwyllys 
7599ebb4caSwyllys 	taglen = kmfber_calc_taglen(tag);
7699ebb4caSwyllys 
7799ebb4caSwyllys 	ntag = htonl(tag);
7899ebb4caSwyllys 
7999ebb4caSwyllys 	return (kmfber_write(ber,
802cbed729Swyllys 	    ((char *) &ntag) + sizeof (ber_int_t) - taglen,
812cbed729Swyllys 	    taglen, nosos));
8299ebb4caSwyllys }
8399ebb4caSwyllys 
8499ebb4caSwyllys int
kmfber_calc_lenlen(ber_int_t len)8599ebb4caSwyllys kmfber_calc_lenlen(ber_int_t len)
8699ebb4caSwyllys {
8799ebb4caSwyllys 	/*
8899ebb4caSwyllys 	 * short len if it's less than 128 - one byte giving the len,
8999ebb4caSwyllys 	 * with bit 8 0.
9099ebb4caSwyllys 	 */
9199ebb4caSwyllys 
9299ebb4caSwyllys 	if (len <= 0x7F)
9399ebb4caSwyllys 		return (1);
9499ebb4caSwyllys 
9599ebb4caSwyllys 	/*
9699ebb4caSwyllys 	 * long len otherwise - one byte with bit 8 set, giving the
9799ebb4caSwyllys 	 * length of the length, followed by the length itself.
9899ebb4caSwyllys 	 */
9999ebb4caSwyllys 
10099ebb4caSwyllys 	if (len <= LENMASK1)
10199ebb4caSwyllys 		return (2);
10299ebb4caSwyllys 	if (len <= LENMASK2)
10399ebb4caSwyllys 		return (3);
10499ebb4caSwyllys 	if (len <= LENMASK3)
10599ebb4caSwyllys 		return (4);
10699ebb4caSwyllys 
10799ebb4caSwyllys 	return (5);
10899ebb4caSwyllys }
10999ebb4caSwyllys 
11099ebb4caSwyllys int
kmfber_put_len(BerElement * ber,ber_int_t len,int nosos)11199ebb4caSwyllys kmfber_put_len(BerElement *ber, ber_int_t len, int nosos)
11299ebb4caSwyllys {
11399ebb4caSwyllys 	int		i;
11499ebb4caSwyllys 	char		lenlen;
11599ebb4caSwyllys 	ber_int_t	mask, netlen;
11699ebb4caSwyllys 
11799ebb4caSwyllys 	/*
11899ebb4caSwyllys 	 * short len if it's less than 128 - one byte giving the len,
11999ebb4caSwyllys 	 * with bit 8 0.
12099ebb4caSwyllys 	 */
12199ebb4caSwyllys 	if (len <= 127) {
12299ebb4caSwyllys 		netlen = htonl(len);
12399ebb4caSwyllys 		return (kmfber_write(ber,
1242cbed729Swyllys 		    (char *)&netlen + sizeof (ber_int_t) - 1,
1252cbed729Swyllys 		    1, nosos));
12699ebb4caSwyllys 	}
12799ebb4caSwyllys 
12899ebb4caSwyllys 	/*
12999ebb4caSwyllys 	 * long len otherwise - one byte with bit 8 set, giving the
13099ebb4caSwyllys 	 * length of the length, followed by the length itself.
13199ebb4caSwyllys 	 */
13299ebb4caSwyllys 
13399ebb4caSwyllys 	/* find the first non-all-zero byte */
13499ebb4caSwyllys 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
13599ebb4caSwyllys 		mask = (LENMASK1 << (i * 8));
13699ebb4caSwyllys 		/* not all zero */
13799ebb4caSwyllys 		if (len & mask)
13899ebb4caSwyllys 			break;
13999ebb4caSwyllys 	}
14099ebb4caSwyllys 	lenlen = ++i;
14199ebb4caSwyllys 	if (lenlen > 4)
14299ebb4caSwyllys 		return (-1);
14399ebb4caSwyllys 	lenlen |= 0x80;
14499ebb4caSwyllys 
14599ebb4caSwyllys 	/* write the length of the length */
14699ebb4caSwyllys 	if (kmfber_write(ber, &lenlen, 1, nosos) != 1)
14799ebb4caSwyllys 		return (-1);
14899ebb4caSwyllys 
14999ebb4caSwyllys 	/* write the length itself */
15099ebb4caSwyllys 	netlen = htonl(len);
15199ebb4caSwyllys 	if (kmfber_write(ber,
1522cbed729Swyllys 	    (char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i)
15399ebb4caSwyllys 		return (-1);
15499ebb4caSwyllys 
15599ebb4caSwyllys 	return (i + 1);
15699ebb4caSwyllys }
15799ebb4caSwyllys 
15899ebb4caSwyllys static int
ber_put_int_or_enum(BerElement * ber,ber_int_t num,ber_tag_t tag)15999ebb4caSwyllys ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
16099ebb4caSwyllys {
16199ebb4caSwyllys 	int		i, sign;
16299ebb4caSwyllys 	ber_int_t	len, lenlen, taglen, netnum, mask;
16399ebb4caSwyllys 
16499ebb4caSwyllys 	sign = (num < 0);
16599ebb4caSwyllys 
16699ebb4caSwyllys 	/*
16799ebb4caSwyllys 	 * high bit is set - look for first non-all-one byte
16899ebb4caSwyllys 	 * high bit is clear - look for first non-all-zero byte
16999ebb4caSwyllys 	 */
17099ebb4caSwyllys 	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
17199ebb4caSwyllys 		mask = (LENMASK1 << (i * 8));
17299ebb4caSwyllys 
17399ebb4caSwyllys 		if (sign) {
17499ebb4caSwyllys 			/* not all ones */
17599ebb4caSwyllys 			if ((num & mask) != mask)
17699ebb4caSwyllys 				break;
17799ebb4caSwyllys 		} else {
17899ebb4caSwyllys 			/* not all zero */
17999ebb4caSwyllys 			if (num & mask)
18099ebb4caSwyllys 				break;
18199ebb4caSwyllys 		}
18299ebb4caSwyllys 	}
18399ebb4caSwyllys 
18499ebb4caSwyllys 	/*
18599ebb4caSwyllys 	 * we now have the "leading byte".  if the high bit on this
18699ebb4caSwyllys 	 * byte matches the sign bit, we need to "back up" a byte.
18799ebb4caSwyllys 	 */
18899ebb4caSwyllys 	mask = (num & (_MASK << (i * 8)));
18999ebb4caSwyllys 	if ((mask && !sign) || (sign && !mask))
19099ebb4caSwyllys 		i++;
19199ebb4caSwyllys 
19299ebb4caSwyllys 	len = i + 1;
19399ebb4caSwyllys 
19499ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
19599ebb4caSwyllys 		return (-1);
19699ebb4caSwyllys 
19799ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1)
19899ebb4caSwyllys 		return (-1);
19999ebb4caSwyllys 	i++;
20099ebb4caSwyllys 	netnum = htonl(num);
20199ebb4caSwyllys 	if (kmfber_write(ber,
2022cbed729Swyllys 	    (char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i)
20399ebb4caSwyllys 		/* length of tag + length + contents */
20499ebb4caSwyllys 		return (taglen + lenlen + i);
20599ebb4caSwyllys 
20699ebb4caSwyllys 	return (-1);
20799ebb4caSwyllys }
20899ebb4caSwyllys 
20999ebb4caSwyllys static int
kmfber_put_enum(BerElement * ber,ber_int_t num,ber_tag_t tag)21099ebb4caSwyllys kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
21199ebb4caSwyllys {
21299ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
21399ebb4caSwyllys 		tag = BER_ENUMERATED;
21499ebb4caSwyllys 
21599ebb4caSwyllys 	return (ber_put_int_or_enum(ber, num, tag));
21699ebb4caSwyllys }
21799ebb4caSwyllys 
21899ebb4caSwyllys int
ber_put_int(BerElement * ber,ber_int_t num,ber_tag_t tag)21999ebb4caSwyllys ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag)
22099ebb4caSwyllys {
22199ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
22299ebb4caSwyllys 		tag = BER_INTEGER;
22399ebb4caSwyllys 
22499ebb4caSwyllys 	return (ber_put_int_or_enum(ber, num, tag));
22599ebb4caSwyllys }
22699ebb4caSwyllys 
22799ebb4caSwyllys int
ber_put_oid(BerElement * ber,struct berval * oid,ber_tag_t tag)22899ebb4caSwyllys ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag)
22999ebb4caSwyllys {
23099ebb4caSwyllys 	ber_int_t taglen, lenlen, rc, len;
23199ebb4caSwyllys 
23299ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
23399ebb4caSwyllys 		tag = 0x06; 	/* TODO: Add new OID constant to header */
23499ebb4caSwyllys 
23599ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
23699ebb4caSwyllys 		return (-1);
23799ebb4caSwyllys 
23899ebb4caSwyllys 	len = (ber_int_t)oid->bv_len;
23999ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 ||
2402cbed729Swyllys 	    kmfber_write(ber, oid->bv_val, oid->bv_len, 0) !=
2412cbed729Swyllys 	    (ber_int_t)oid->bv_len) {
24299ebb4caSwyllys 		rc = -1;
24399ebb4caSwyllys 	} else {
24499ebb4caSwyllys 		/* return length of tag + length + contents */
24599ebb4caSwyllys 		rc = taglen + lenlen + oid->bv_len;
24699ebb4caSwyllys 	}
24799ebb4caSwyllys 	return (rc);
24899ebb4caSwyllys }
24999ebb4caSwyllys 
25099ebb4caSwyllys int
ber_put_big_int(BerElement * ber,ber_tag_t tag,char * data,ber_len_t len)25199ebb4caSwyllys ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data,
25299ebb4caSwyllys 	ber_len_t len)
25399ebb4caSwyllys {
25499ebb4caSwyllys 	ber_int_t taglen, lenlen, ilen, rc;
2552cbed729Swyllys 	char zero = 0x00;
25699ebb4caSwyllys 
25799ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
25899ebb4caSwyllys 		tag = BER_INTEGER;
25999ebb4caSwyllys 
26099ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
26199ebb4caSwyllys 		return (-1);
26299ebb4caSwyllys 
2632cbed729Swyllys 	/* Add a leading 0 if the high order bit is set */
2642cbed729Swyllys 	if (data[0] & 0x80)
2652cbed729Swyllys 		len++;
2662cbed729Swyllys 
26799ebb4caSwyllys 	ilen = (ber_int_t)len;
2682cbed729Swyllys 	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1)
2692cbed729Swyllys 		return (-1);
2702cbed729Swyllys 
2712cbed729Swyllys 	/* add leading 0 if hi bit set */
2722cbed729Swyllys 	if ((data[0] & 0x80) && kmfber_write(ber, &zero, 1, 0) != 1)
2732cbed729Swyllys 		return (-1);
2742cbed729Swyllys 
2752cbed729Swyllys 	/* Adjust the length of the write if hi-order bit is set */
2762cbed729Swyllys 	if (data[0] & 0x80)
2772cbed729Swyllys 		ilen = len - 1;
2782cbed729Swyllys 	if (kmfber_write(ber, data, ilen, 0) != (ber_int_t)ilen) {
2792cbed729Swyllys 		return (-1);
28099ebb4caSwyllys 	} else {
28199ebb4caSwyllys 		/* return length of tag + length + contents */
28299ebb4caSwyllys 		rc = taglen + lenlen + len;
28399ebb4caSwyllys 	}
28499ebb4caSwyllys 	return (rc);
28599ebb4caSwyllys }
28699ebb4caSwyllys 
28799ebb4caSwyllys static int
kmfber_put_ostring(BerElement * ber,char * str,ber_len_t len,ber_tag_t tag)28899ebb4caSwyllys kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len,
28999ebb4caSwyllys 	ber_tag_t tag)
29099ebb4caSwyllys {
29199ebb4caSwyllys 	ber_int_t	taglen, lenlen, ilen, rc;
29299ebb4caSwyllys #ifdef STR_TRANSLATION
29399ebb4caSwyllys 	int	free_str;
29499ebb4caSwyllys #endif /* STR_TRANSLATION */
29599ebb4caSwyllys 
29699ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
29799ebb4caSwyllys 		tag = BER_OCTET_STRING;
29899ebb4caSwyllys 
29999ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
30099ebb4caSwyllys 		return (-1);
30199ebb4caSwyllys 
30299ebb4caSwyllys #ifdef STR_TRANSLATION
30399ebb4caSwyllys 	if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 &&
30499ebb4caSwyllys 	    ber->ber_encode_translate_proc != NULL) {
30599ebb4caSwyllys 		if ((*(ber->ber_encode_translate_proc))(&str, &len, 0)
30699ebb4caSwyllys 		    != 0) {
30799ebb4caSwyllys 			return (-1);
30899ebb4caSwyllys 		}
30999ebb4caSwyllys 		free_str = 1;
31099ebb4caSwyllys 	} else {
31199ebb4caSwyllys 		free_str = 0;
31299ebb4caSwyllys 	}
31399ebb4caSwyllys #endif /* STR_TRANSLATION */
31499ebb4caSwyllys 
31599ebb4caSwyllys 	/*
31699ebb4caSwyllys 	 *  Note:  below is a spot where we limit ber_write
31799ebb4caSwyllys 	 *	to signed long (instead of unsigned long)
31899ebb4caSwyllys 	 */
31999ebb4caSwyllys 	ilen = (ber_int_t)len;
32099ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
3212cbed729Swyllys 	    kmfber_write(ber, str, len, 0) != (ber_int_t)len) {
32299ebb4caSwyllys 		rc = -1;
32399ebb4caSwyllys 	} else {
32499ebb4caSwyllys 		/* return length of tag + length + contents */
32599ebb4caSwyllys 		rc = taglen + lenlen + len;
32699ebb4caSwyllys 	}
32799ebb4caSwyllys 
32899ebb4caSwyllys #ifdef STR_TRANSLATION
32999ebb4caSwyllys 	if (free_str) {
33099ebb4caSwyllys 		free(str);
33199ebb4caSwyllys 	}
33299ebb4caSwyllys #endif /* STR_TRANSLATION */
33399ebb4caSwyllys 
33499ebb4caSwyllys 	return (rc);
33599ebb4caSwyllys }
33699ebb4caSwyllys 
33799ebb4caSwyllys static int
kmfber_put_string(BerElement * ber,char * str,ber_tag_t tag)33899ebb4caSwyllys kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag)
33999ebb4caSwyllys {
34099ebb4caSwyllys 	return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag));
34199ebb4caSwyllys }
34299ebb4caSwyllys 
34399ebb4caSwyllys static int
kmfber_put_bitstring(BerElement * ber,char * str,ber_len_t blen,ber_tag_t tag)34499ebb4caSwyllys kmfber_put_bitstring(BerElement *ber, char *str,
34599ebb4caSwyllys 	ber_len_t blen /* in bits */, ber_tag_t tag)
34699ebb4caSwyllys {
34799ebb4caSwyllys 	ber_int_t	taglen, lenlen, len;
34899ebb4caSwyllys 	unsigned char	unusedbits;
34999ebb4caSwyllys 
35099ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
35199ebb4caSwyllys 		tag = BER_BIT_STRING;
35299ebb4caSwyllys 
35399ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
35499ebb4caSwyllys 		return (-1);
35599ebb4caSwyllys 
35699ebb4caSwyllys 	len = (blen + 7) / 8;
35799ebb4caSwyllys 	unusedbits = (unsigned char) (len * 8 - blen);
35899ebb4caSwyllys 	if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1)
35999ebb4caSwyllys 		return (-1);
36099ebb4caSwyllys 
36199ebb4caSwyllys 	if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1)
36299ebb4caSwyllys 		return (-1);
36399ebb4caSwyllys 
36499ebb4caSwyllys 	if (kmfber_write(ber, str, len, 0) != len)
36599ebb4caSwyllys 		return (-1);
36699ebb4caSwyllys 
36799ebb4caSwyllys 	/* return length of tag + length + unused bit count + contents */
36899ebb4caSwyllys 	return (taglen + 1 + lenlen + len);
36999ebb4caSwyllys }
37099ebb4caSwyllys 
37199ebb4caSwyllys static int
kmfber_put_null(BerElement * ber,ber_tag_t tag)37299ebb4caSwyllys kmfber_put_null(BerElement *ber, ber_tag_t tag)
37399ebb4caSwyllys {
37499ebb4caSwyllys 	int	taglen;
37599ebb4caSwyllys 
37699ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
37799ebb4caSwyllys 		tag = BER_NULL;
37899ebb4caSwyllys 
37999ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
38099ebb4caSwyllys 		return (-1);
38199ebb4caSwyllys 
38299ebb4caSwyllys 	if (kmfber_put_len(ber, 0, 0) != 1)
38399ebb4caSwyllys 		return (-1);
38499ebb4caSwyllys 
38599ebb4caSwyllys 	return (taglen + 1);
38699ebb4caSwyllys }
38799ebb4caSwyllys 
38899ebb4caSwyllys static int
kmfber_put_boolean(BerElement * ber,int boolval,ber_tag_t tag)38999ebb4caSwyllys kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag)
39099ebb4caSwyllys {
39199ebb4caSwyllys 	int		taglen;
39299ebb4caSwyllys 	unsigned char	trueval = 0xff;
39399ebb4caSwyllys 	unsigned char	falseval = 0x00;
39499ebb4caSwyllys 
39599ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
39699ebb4caSwyllys 		tag = BER_BOOLEAN;
39799ebb4caSwyllys 
39899ebb4caSwyllys 	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
39999ebb4caSwyllys 		return (-1);
40099ebb4caSwyllys 
40199ebb4caSwyllys 	if (kmfber_put_len(ber, 1, 0) != 1)
40299ebb4caSwyllys 		return (-1);
40399ebb4caSwyllys 
40499ebb4caSwyllys 	if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0)
40599ebb4caSwyllys 	    != 1)
40699ebb4caSwyllys 		return (-1);
40799ebb4caSwyllys 
40899ebb4caSwyllys 	return (taglen + 2);
40999ebb4caSwyllys }
41099ebb4caSwyllys 
41199ebb4caSwyllys #define	FOUR_BYTE_LEN	5
41299ebb4caSwyllys 
41399ebb4caSwyllys 
41499ebb4caSwyllys /*
41599ebb4caSwyllys  * The idea here is roughly this: we maintain a stack of these Seqorset
41699ebb4caSwyllys  * structures. This is pushed when we see the beginning of a new set or
41799ebb4caSwyllys  * sequence. It is popped when we see the end of a set or sequence.
41899ebb4caSwyllys  * Since we don't want to malloc and free these structures all the time,
41999ebb4caSwyllys  * we pre-allocate a small set of them within the ber element structure.
42099ebb4caSwyllys  * thus we need to spot when we've overflowed this stack and fall back to
42199ebb4caSwyllys  * malloc'ing instead.
42299ebb4caSwyllys  */
42399ebb4caSwyllys static int
ber_start_seqorset(BerElement * ber,ber_tag_t tag)42499ebb4caSwyllys ber_start_seqorset(BerElement *ber, ber_tag_t tag)
42599ebb4caSwyllys {
42699ebb4caSwyllys 	Seqorset	*new_sos;
42799ebb4caSwyllys 
42899ebb4caSwyllys 	/* can we fit into the local stack ? */
42999ebb4caSwyllys 	if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
43099ebb4caSwyllys 		/* yes */
43199ebb4caSwyllys 		new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
43299ebb4caSwyllys 	} else {
43399ebb4caSwyllys 		/* no */
43499ebb4caSwyllys 		if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset)))
43599ebb4caSwyllys 		    == NULLSEQORSET) {
43699ebb4caSwyllys 			return (-1);
43799ebb4caSwyllys 		}
43899ebb4caSwyllys 	}
43999ebb4caSwyllys 	ber->ber_sos_stack_posn++;
44099ebb4caSwyllys 
44199ebb4caSwyllys 	if (ber->ber_sos == NULLSEQORSET)
44299ebb4caSwyllys 		new_sos->sos_first = ber->ber_ptr;
44399ebb4caSwyllys 	else
44499ebb4caSwyllys 		new_sos->sos_first = ber->ber_sos->sos_ptr;
44599ebb4caSwyllys 
44699ebb4caSwyllys 	/* Set aside room for a 4 byte length field */
44799ebb4caSwyllys 	new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) +
4482cbed729Swyllys 	    FOUR_BYTE_LEN;
44999ebb4caSwyllys 	new_sos->sos_tag = tag;
45099ebb4caSwyllys 
45199ebb4caSwyllys 	new_sos->sos_next = ber->ber_sos;
45299ebb4caSwyllys 	new_sos->sos_clen = 0;
45399ebb4caSwyllys 
45499ebb4caSwyllys 	ber->ber_sos = new_sos;
45599ebb4caSwyllys 	if (ber->ber_sos->sos_ptr > ber->ber_end) {
456*56664548SWyllys Ingersoll 		if (kmfber_realloc(ber, ber->ber_sos->sos_ptr -
457*56664548SWyllys Ingersoll 		    ber->ber_end) != 0)
458*56664548SWyllys Ingersoll 			return (-1);
45999ebb4caSwyllys 	}
46099ebb4caSwyllys 	return (0);
46199ebb4caSwyllys }
46299ebb4caSwyllys 
46399ebb4caSwyllys static int
kmfber_start_seq(BerElement * ber,ber_tag_t tag)46499ebb4caSwyllys kmfber_start_seq(BerElement *ber, ber_tag_t tag)
46599ebb4caSwyllys {
46699ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
46799ebb4caSwyllys 		tag = BER_CONSTRUCTED_SEQUENCE;
46899ebb4caSwyllys 
46999ebb4caSwyllys 	return (ber_start_seqorset(ber, tag));
47099ebb4caSwyllys }
47199ebb4caSwyllys 
47299ebb4caSwyllys static int
kmfber_start_set(BerElement * ber,ber_tag_t tag)47399ebb4caSwyllys kmfber_start_set(BerElement *ber, ber_tag_t tag)
47499ebb4caSwyllys {
47599ebb4caSwyllys 	if (tag == KMFBER_DEFAULT)
47699ebb4caSwyllys 		tag = BER_CONSTRUCTED_SET;
47799ebb4caSwyllys 
47899ebb4caSwyllys 	return (ber_start_seqorset(ber, tag));
47999ebb4caSwyllys }
48099ebb4caSwyllys 
48199ebb4caSwyllys static int
ber_put_seqorset(BerElement * ber)48299ebb4caSwyllys ber_put_seqorset(BerElement *ber)
48399ebb4caSwyllys {
48499ebb4caSwyllys 	ber_int_t	netlen, len, taglen, lenlen;
48599ebb4caSwyllys 	unsigned char	ltag = 0x80 + FOUR_BYTE_LEN - 1;
48699ebb4caSwyllys 	Seqorset	*next;
48799ebb4caSwyllys 	Seqorset	**sos = &ber->ber_sos;
48899ebb4caSwyllys 
48999ebb4caSwyllys 	/*
49099ebb4caSwyllys 	 * If this is the toplevel sequence or set, we need to actually
49199ebb4caSwyllys 	 * write the stuff out.  Otherwise, it's already been put in
49299ebb4caSwyllys 	 * the appropriate buffer and will be written when the toplevel
49399ebb4caSwyllys 	 * one is written.  In this case all we need to do is update the
49499ebb4caSwyllys 	 * length and tag.
49599ebb4caSwyllys 	 */
49699ebb4caSwyllys 
49799ebb4caSwyllys 	len = (*sos)->sos_clen;
49899ebb4caSwyllys 	netlen = (ber_len_t)htonl(len);
49999ebb4caSwyllys 
50099ebb4caSwyllys 	if (ber->ber_options & KMFBER_OPT_USE_DER) {
50199ebb4caSwyllys 		lenlen = kmfber_calc_lenlen(len);
50299ebb4caSwyllys 	} else {
50399ebb4caSwyllys 		lenlen = FOUR_BYTE_LEN;
50499ebb4caSwyllys 	}
50599ebb4caSwyllys 
50699ebb4caSwyllys 	if ((next = (*sos)->sos_next) == NULLSEQORSET) {
50799ebb4caSwyllys 		/* write the tag */
50899ebb4caSwyllys 		if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1)
50999ebb4caSwyllys 			return (-1);
51099ebb4caSwyllys 
51199ebb4caSwyllys 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
51299ebb4caSwyllys 			/* Write the length in the minimum # of octets */
51399ebb4caSwyllys 			if (kmfber_put_len(ber, len, 1) == -1)
51499ebb4caSwyllys 				return (-1);
51599ebb4caSwyllys 
51699ebb4caSwyllys 			if (lenlen != FOUR_BYTE_LEN) {
51799ebb4caSwyllys 				/*
51899ebb4caSwyllys 				 * We set aside FOUR_BYTE_LEN bytes for
51999ebb4caSwyllys 				 * the length field.  Move the data if
52099ebb4caSwyllys 				 * we don't actually need that much
52199ebb4caSwyllys 				 */
52299ebb4caSwyllys 				(void) memmove((*sos)->sos_first + taglen +
52399ebb4caSwyllys 				    lenlen, (*sos)->sos_first + taglen +
52499ebb4caSwyllys 				    FOUR_BYTE_LEN, len);
52599ebb4caSwyllys 			}
52699ebb4caSwyllys 		} else {
52799ebb4caSwyllys 			/* Fill FOUR_BYTE_LEN bytes for length field */
52899ebb4caSwyllys 			/* one byte of length length */
52999ebb4caSwyllys 			if (kmfber_write(ber, (char *)&ltag, 1, 1) != 1)
53099ebb4caSwyllys 				return (-1);
53199ebb4caSwyllys 
53299ebb4caSwyllys 			/* the length itself */
53399ebb4caSwyllys 			if (kmfber_write(ber,
5342cbed729Swyllys 			    (char *)&netlen + sizeof (ber_int_t)
5352cbed729Swyllys 			    - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) !=
5362cbed729Swyllys 			    FOUR_BYTE_LEN - 1)
53799ebb4caSwyllys 				return (-1);
53899ebb4caSwyllys 		}
53999ebb4caSwyllys 		/* The ber_ptr is at the set/seq start - move it to the end */
54099ebb4caSwyllys 		ber->ber_ptr += len;
54199ebb4caSwyllys 	} else {
54299ebb4caSwyllys 		ber_tag_t	ntag;
54399ebb4caSwyllys 
54499ebb4caSwyllys 		/* the tag */
54599ebb4caSwyllys 		taglen = kmfber_calc_taglen((*sos)->sos_tag);
54699ebb4caSwyllys 		ntag = htonl((*sos)->sos_tag);
54799ebb4caSwyllys 		(void) memmove((*sos)->sos_first, (char *)&ntag +
54899ebb4caSwyllys 		    sizeof (ber_int_t) - taglen, taglen);
54999ebb4caSwyllys 
55099ebb4caSwyllys 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
55199ebb4caSwyllys 			ltag = (lenlen == 1) ? (unsigned char)len :
5522cbed729Swyllys 			    (unsigned char) (0x80 + (lenlen - 1));
55399ebb4caSwyllys 		}
55499ebb4caSwyllys 
55599ebb4caSwyllys 		/* one byte of length length */
55699ebb4caSwyllys 		(void) memmove((*sos)->sos_first + 1, &ltag, 1);
55799ebb4caSwyllys 
55899ebb4caSwyllys 		if (ber->ber_options & KMFBER_OPT_USE_DER) {
55999ebb4caSwyllys 			if (lenlen > 1) {
56099ebb4caSwyllys 				/* Write the length itself */
56199ebb4caSwyllys 				(void) memmove((*sos)->sos_first + 2,
56299ebb4caSwyllys 				    (char *)&netlen + sizeof (ber_uint_t) -
56399ebb4caSwyllys 				    (lenlen - 1),
56499ebb4caSwyllys 				    lenlen - 1);
56599ebb4caSwyllys 			}
56699ebb4caSwyllys 			if (lenlen != FOUR_BYTE_LEN) {
56799ebb4caSwyllys 				/*
56899ebb4caSwyllys 				 * We set aside FOUR_BYTE_LEN bytes for
56999ebb4caSwyllys 				 * the length field.  Move the data if
57099ebb4caSwyllys 				 * we don't actually need that much
57199ebb4caSwyllys 				 */
57299ebb4caSwyllys 				(void) memmove((*sos)->sos_first + taglen +
57399ebb4caSwyllys 				    lenlen, (*sos)->sos_first + taglen +
57499ebb4caSwyllys 				    FOUR_BYTE_LEN, len);
57599ebb4caSwyllys 			}
57699ebb4caSwyllys 		} else {
57799ebb4caSwyllys 			/* the length itself */
57899ebb4caSwyllys 			(void) memmove((*sos)->sos_first + taglen + 1,
57999ebb4caSwyllys 			    (char *) &netlen + sizeof (ber_int_t) -
58099ebb4caSwyllys 			    (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1);
58199ebb4caSwyllys 		}
58299ebb4caSwyllys 
58399ebb4caSwyllys 		next->sos_clen += (taglen + lenlen + len);
58499ebb4caSwyllys 		next->sos_ptr += (taglen + lenlen + len);
58599ebb4caSwyllys 	}
58699ebb4caSwyllys 
58799ebb4caSwyllys 	/* we're done with this seqorset, so free it up */
58899ebb4caSwyllys 	/* was this one from the local stack ? */
58999ebb4caSwyllys 	if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) {
59099ebb4caSwyllys 		free((char *)(*sos));
59199ebb4caSwyllys 	}
59299ebb4caSwyllys 	ber->ber_sos_stack_posn--;
59399ebb4caSwyllys 	*sos = next;
59499ebb4caSwyllys 
59599ebb4caSwyllys 	return (taglen + lenlen + len);
59699ebb4caSwyllys }
59799ebb4caSwyllys 
59899ebb4caSwyllys /* VARARGS */
59999ebb4caSwyllys int
kmfber_printf(BerElement * ber,const char * fmt,...)60099ebb4caSwyllys kmfber_printf(BerElement *ber, const char *fmt, ...)
60199ebb4caSwyllys {
60299ebb4caSwyllys 	va_list		ap;
60399ebb4caSwyllys 	char		*s, **ss;
60499ebb4caSwyllys 	struct berval	**bv, *oid;
60599ebb4caSwyllys 	int		rc, i, t;
60699ebb4caSwyllys 	ber_int_t	len;
60799ebb4caSwyllys 
60899ebb4caSwyllys 	va_start(ap, fmt);
60999ebb4caSwyllys 
61099ebb4caSwyllys #ifdef KMFBER_DEBUG
61199ebb4caSwyllys 	if (lber_debug & 64) {
61299ebb4caSwyllys 		char msg[80];
61399ebb4caSwyllys 		sprintf(msg, "kmfber_printf fmt (%s)\n", fmt);
61499ebb4caSwyllys 		ber_err_print(msg);
61599ebb4caSwyllys 	}
61699ebb4caSwyllys #endif
61799ebb4caSwyllys 
61899ebb4caSwyllys 	for (rc = 0; *fmt && rc != -1; fmt++) {
61999ebb4caSwyllys 		switch (*fmt) {
62099ebb4caSwyllys 		case 'b':	/* boolean */
62199ebb4caSwyllys 			i = va_arg(ap, int);
62299ebb4caSwyllys 			rc = kmfber_put_boolean(ber, i, ber->ber_tag);
62399ebb4caSwyllys 			break;
62499ebb4caSwyllys 
62599ebb4caSwyllys 		case 'i':	/* int */
62699ebb4caSwyllys 			i = va_arg(ap, int);
62799ebb4caSwyllys 			rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag);
62899ebb4caSwyllys 			break;
62999ebb4caSwyllys 
63099ebb4caSwyllys 		case 'D':	/* Object ID */
63199ebb4caSwyllys 			if ((oid = va_arg(ap, struct berval *)) == NULL)
63299ebb4caSwyllys 				break;
63399ebb4caSwyllys 			rc = ber_put_oid(ber, oid, ber->ber_tag);
63499ebb4caSwyllys 			break;
63599ebb4caSwyllys 		case 'I':	/* int */
63699ebb4caSwyllys 			s = va_arg(ap, char *);
63799ebb4caSwyllys 			len = va_arg(ap, ber_int_t);
63899ebb4caSwyllys 			rc = ber_put_big_int(ber, ber->ber_tag, s, len);
63999ebb4caSwyllys 			break;
64099ebb4caSwyllys 
64199ebb4caSwyllys 		case 'e':	/* enumeration */
64299ebb4caSwyllys 			i = va_arg(ap, int);
64399ebb4caSwyllys 			rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag);
64499ebb4caSwyllys 			break;
64599ebb4caSwyllys 
64699ebb4caSwyllys 		case 'l':
64799ebb4caSwyllys 			t = va_arg(ap, int);
64899ebb4caSwyllys 			rc = kmfber_put_len(ber, t, 0);
64999ebb4caSwyllys 			break;
65099ebb4caSwyllys 		case 'n':	/* null */
65199ebb4caSwyllys 			rc = kmfber_put_null(ber, ber->ber_tag);
65299ebb4caSwyllys 			break;
65399ebb4caSwyllys 
65499ebb4caSwyllys 		case 'o':	/* octet string (non-null terminated) */
65599ebb4caSwyllys 			s = va_arg(ap, char *);
65699ebb4caSwyllys 			len = va_arg(ap, int);
65799ebb4caSwyllys 			rc = kmfber_put_ostring(ber, s, len, ber->ber_tag);
65899ebb4caSwyllys 			break;
65999ebb4caSwyllys 
66099ebb4caSwyllys 		case 's':	/* string */
66199ebb4caSwyllys 			s = va_arg(ap, char *);
66299ebb4caSwyllys 			rc = kmfber_put_string(ber, s, ber->ber_tag);
66399ebb4caSwyllys 			break;
66499ebb4caSwyllys 
66599ebb4caSwyllys 		case 'B':	/* bit string */
66699ebb4caSwyllys 			s = va_arg(ap, char *);
66799ebb4caSwyllys 			len = va_arg(ap, int);	/* in bits */
66899ebb4caSwyllys 			rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag);
66999ebb4caSwyllys 			break;
67099ebb4caSwyllys 
67199ebb4caSwyllys 		case 't':	/* tag for the next element */
67299ebb4caSwyllys 			ber->ber_tag = va_arg(ap, ber_tag_t);
67399ebb4caSwyllys 			ber->ber_usertag = 1;
67499ebb4caSwyllys 			break;
67599ebb4caSwyllys 
67699ebb4caSwyllys 		case 'T': /* Write an explicit tag, but don't change current */
67799ebb4caSwyllys 			t = va_arg(ap, int);
67899ebb4caSwyllys 			rc = ber_put_tag(ber, t, 0);
67999ebb4caSwyllys 			break;
68099ebb4caSwyllys 
68199ebb4caSwyllys 		case 'v':	/* vector of strings */
68299ebb4caSwyllys 			if ((ss = va_arg(ap, char **)) == NULL)
68399ebb4caSwyllys 				break;
68499ebb4caSwyllys 			for (i = 0; ss[i] != NULL; i++) {
68599ebb4caSwyllys 				if ((rc = kmfber_put_string(ber, ss[i],
68699ebb4caSwyllys 				    ber->ber_tag)) == -1)
68799ebb4caSwyllys 					break;
68899ebb4caSwyllys 			}
68999ebb4caSwyllys 			break;
69099ebb4caSwyllys 
69199ebb4caSwyllys 		case 'V':	/* sequences of strings + lengths */
69299ebb4caSwyllys 			if ((bv = va_arg(ap, struct berval **)) == NULL)
69399ebb4caSwyllys 				break;
69499ebb4caSwyllys 			for (i = 0; bv[i] != NULL; i++) {
69599ebb4caSwyllys 				if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val,
69699ebb4caSwyllys 				    bv[i]->bv_len, ber->ber_tag)) == -1)
69799ebb4caSwyllys 					break;
69899ebb4caSwyllys 			}
69999ebb4caSwyllys 			break;
70099ebb4caSwyllys 
70199ebb4caSwyllys 		case '{':	/* begin sequence */
70299ebb4caSwyllys 			rc = kmfber_start_seq(ber, ber->ber_tag);
70399ebb4caSwyllys 			break;
70499ebb4caSwyllys 
70599ebb4caSwyllys 		case '}':	/* end sequence */
70699ebb4caSwyllys 			rc = ber_put_seqorset(ber);
70799ebb4caSwyllys 			break;
70899ebb4caSwyllys 
70999ebb4caSwyllys 		case '[':	/* begin set */
71099ebb4caSwyllys 			rc = kmfber_start_set(ber, ber->ber_tag);
71199ebb4caSwyllys 			break;
71299ebb4caSwyllys 
71399ebb4caSwyllys 		case ']':	/* end set */
71499ebb4caSwyllys 			rc = ber_put_seqorset(ber);
71599ebb4caSwyllys 			break;
71699ebb4caSwyllys 
71799ebb4caSwyllys 		default: {
71899ebb4caSwyllys #ifdef KMFBER_DEBUG
71999ebb4caSwyllys 				char msg[80];
72099ebb4caSwyllys 				sprintf(msg, "unknown fmt %c\n", *fmt);
72199ebb4caSwyllys 				ber_err_print(msg);
72299ebb4caSwyllys #endif
72399ebb4caSwyllys 				rc = -1;
72499ebb4caSwyllys 				break;
72599ebb4caSwyllys 			}
72699ebb4caSwyllys 		}
72799ebb4caSwyllys 
72899ebb4caSwyllys 		if (ber->ber_usertag == 0)
72999ebb4caSwyllys 			ber->ber_tag = KMFBER_DEFAULT;
73099ebb4caSwyllys 		else
73199ebb4caSwyllys 			ber->ber_usertag = 0;
73299ebb4caSwyllys 	}
73399ebb4caSwyllys 
73499ebb4caSwyllys 	va_end(ap);
73599ebb4caSwyllys 
73699ebb4caSwyllys 	return (rc);
73799ebb4caSwyllys }
738