summaryrefslogtreecommitdiff
path: root/usr/src/lib/libkmf/ber_der/common/encode.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libkmf/ber_der/common/encode.c')
-rw-r--r--usr/src/lib/libkmf/ber_der/common/encode.c744
1 files changed, 744 insertions, 0 deletions
diff --git a/usr/src/lib/libkmf/ber_der/common/encode.c b/usr/src/lib/libkmf/ber_der/common/encode.c
new file mode 100644
index 0000000000..bf78d8e719
--- /dev/null
+++ b/usr/src/lib/libkmf/ber_der/common/encode.c
@@ -0,0 +1,744 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+
+#include <ber_der.h>
+#include "kmfber_int.h"
+
+/* the following constants are used in kmfber_calc_lenlen */
+
+#define LENMASK1 0xFF
+#define LENMASK2 0xFFFF
+#define LENMASK3 0xFFFFFF
+#define LENMASK4 0xFFFFFFFF
+#define _MASK 0x80
+
+int
+kmfber_calc_taglen(ber_tag_t tag)
+{
+ int i;
+ ber_int_t mask;
+
+ /* find the first non-all-zero byte in the tag */
+ for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
+ mask = (LENMASK3 << (i * 8));
+ /* not all zero */
+ if (tag & mask)
+ break;
+ }
+
+ return (i + 1);
+}
+
+static int
+ber_put_tag(BerElement *ber, ber_tag_t tag, int nosos)
+{
+ ber_int_t taglen;
+ ber_tag_t ntag;
+
+ taglen = kmfber_calc_taglen(tag);
+
+ ntag = htonl(tag);
+
+ return (kmfber_write(ber,
+ ((char *) &ntag) + sizeof (ber_int_t) - taglen,
+ taglen, nosos));
+}
+
+int
+kmfber_calc_lenlen(ber_int_t len)
+{
+ /*
+ * short len if it's less than 128 - one byte giving the len,
+ * with bit 8 0.
+ */
+
+ if (len <= 0x7F)
+ return (1);
+
+ /*
+ * long len otherwise - one byte with bit 8 set, giving the
+ * length of the length, followed by the length itself.
+ */
+
+ if (len <= LENMASK1)
+ return (2);
+ if (len <= LENMASK2)
+ return (3);
+ if (len <= LENMASK3)
+ return (4);
+
+ return (5);
+}
+
+int
+kmfber_put_len(BerElement *ber, ber_int_t len, int nosos)
+{
+ int i;
+ char lenlen;
+ ber_int_t mask, netlen;
+
+ /*
+ * short len if it's less than 128 - one byte giving the len,
+ * with bit 8 0.
+ */
+ if (len <= 127) {
+ netlen = htonl(len);
+ return (kmfber_write(ber,
+ (char *)&netlen + sizeof (ber_int_t) - 1,
+ 1, nosos));
+ }
+
+ /*
+ * long len otherwise - one byte with bit 8 set, giving the
+ * length of the length, followed by the length itself.
+ */
+
+ /* find the first non-all-zero byte */
+ for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
+ mask = (LENMASK1 << (i * 8));
+ /* not all zero */
+ if (len & mask)
+ break;
+ }
+ lenlen = ++i;
+ if (lenlen > 4)
+ return (-1);
+ lenlen |= 0x80;
+
+ /* write the length of the length */
+ if (kmfber_write(ber, &lenlen, 1, nosos) != 1)
+ return (-1);
+
+ /* write the length itself */
+ netlen = htonl(len);
+ if (kmfber_write(ber,
+ (char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i)
+ return (-1);
+
+ return (i + 1);
+}
+
+static int
+ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
+{
+ int i, sign;
+ ber_int_t len, lenlen, taglen, netnum, mask;
+
+ sign = (num < 0);
+
+ /*
+ * high bit is set - look for first non-all-one byte
+ * high bit is clear - look for first non-all-zero byte
+ */
+ for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
+ mask = (LENMASK1 << (i * 8));
+
+ if (sign) {
+ /* not all ones */
+ if ((num & mask) != mask)
+ break;
+ } else {
+ /* not all zero */
+ if (num & mask)
+ break;
+ }
+ }
+
+ /*
+ * we now have the "leading byte". if the high bit on this
+ * byte matches the sign bit, we need to "back up" a byte.
+ */
+ mask = (num & (_MASK << (i * 8)));
+ if ((mask && !sign) || (sign && !mask))
+ i++;
+
+ len = i + 1;
+
+ if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
+ return (-1);
+
+ if ((lenlen = kmfber_put_len(ber, len, 0)) == -1)
+ return (-1);
+ i++;
+ netnum = htonl(num);
+ if (kmfber_write(ber,
+ (char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i)
+ /* length of tag + length + contents */
+ return (taglen + lenlen + i);
+
+ return (-1);
+}
+
+static int
+kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
+{
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_ENUMERATED;
+
+ return (ber_put_int_or_enum(ber, num, tag));
+}
+
+int
+ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag)
+{
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_INTEGER;
+
+ return (ber_put_int_or_enum(ber, num, tag));
+}
+
+int
+ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag)
+{
+ ber_int_t taglen, lenlen, rc, len;
+
+ if (tag == KMFBER_DEFAULT)
+ tag = 0x06; /* TODO: Add new OID constant to header */
+
+ if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
+ return (-1);
+
+ len = (ber_int_t)oid->bv_len;
+ if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 ||
+ kmfber_write(ber, oid->bv_val, oid->bv_len, 0) !=
+ (ber_int_t)oid->bv_len) {
+ rc = -1;
+ } else {
+ /* return length of tag + length + contents */
+ rc = taglen + lenlen + oid->bv_len;
+ }
+ return (rc);
+}
+
+int
+ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data,
+ ber_len_t len)
+{
+ ber_int_t taglen, lenlen, ilen, rc;
+
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_INTEGER;
+
+ if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
+ return (-1);
+
+ ilen = (ber_int_t)len;
+ if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
+ kmfber_write(ber, data, len, 0) != (ber_int_t)len) {
+ rc = -1;
+ } else {
+ /* return length of tag + length + contents */
+ rc = taglen + lenlen + len;
+ }
+ return (rc);
+}
+
+static int
+kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len,
+ ber_tag_t tag)
+{
+ ber_int_t taglen, lenlen, ilen, rc;
+#ifdef STR_TRANSLATION
+ int free_str;
+#endif /* STR_TRANSLATION */
+
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_OCTET_STRING;
+
+ if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
+ return (-1);
+
+#ifdef STR_TRANSLATION
+ if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 &&
+ ber->ber_encode_translate_proc != NULL) {
+ if ((*(ber->ber_encode_translate_proc))(&str, &len, 0)
+ != 0) {
+ return (-1);
+ }
+ free_str = 1;
+ } else {
+ free_str = 0;
+ }
+#endif /* STR_TRANSLATION */
+
+ /*
+ * Note: below is a spot where we limit ber_write
+ * to signed long (instead of unsigned long)
+ */
+ ilen = (ber_int_t)len;
+ if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
+ kmfber_write(ber, str, len, 0) != (ber_int_t)len) {
+ rc = -1;
+ } else {
+ /* return length of tag + length + contents */
+ rc = taglen + lenlen + len;
+ }
+
+#ifdef STR_TRANSLATION
+ if (free_str) {
+ free(str);
+ }
+#endif /* STR_TRANSLATION */
+
+ return (rc);
+}
+
+static int
+kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag)
+{
+ return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag));
+}
+
+static int
+kmfber_put_bitstring(BerElement *ber, char *str,
+ ber_len_t blen /* in bits */, ber_tag_t tag)
+{
+ ber_int_t taglen, lenlen, len;
+ unsigned char unusedbits;
+
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_BIT_STRING;
+
+ if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
+ return (-1);
+
+ len = (blen + 7) / 8;
+ unusedbits = (unsigned char) (len * 8 - blen);
+ if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1)
+ return (-1);
+
+ if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1)
+ return (-1);
+
+ if (kmfber_write(ber, str, len, 0) != len)
+ return (-1);
+
+ /* return length of tag + length + unused bit count + contents */
+ return (taglen + 1 + lenlen + len);
+}
+
+static int
+kmfber_put_null(BerElement *ber, ber_tag_t tag)
+{
+ int taglen;
+
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_NULL;
+
+ if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
+ return (-1);
+
+ if (kmfber_put_len(ber, 0, 0) != 1)
+ return (-1);
+
+ return (taglen + 1);
+}
+
+static int
+kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag)
+{
+ int taglen;
+ unsigned char trueval = 0xff;
+ unsigned char falseval = 0x00;
+
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_BOOLEAN;
+
+ if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
+ return (-1);
+
+ if (kmfber_put_len(ber, 1, 0) != 1)
+ return (-1);
+
+ if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0)
+ != 1)
+ return (-1);
+
+ return (taglen + 2);
+}
+
+#define FOUR_BYTE_LEN 5
+
+
+/*
+ * The idea here is roughly this: we maintain a stack of these Seqorset
+ * structures. This is pushed when we see the beginning of a new set or
+ * sequence. It is popped when we see the end of a set or sequence.
+ * Since we don't want to malloc and free these structures all the time,
+ * we pre-allocate a small set of them within the ber element structure.
+ * thus we need to spot when we've overflowed this stack and fall back to
+ * malloc'ing instead.
+ */
+static int
+ber_start_seqorset(BerElement *ber, ber_tag_t tag)
+{
+ Seqorset *new_sos;
+
+ /* can we fit into the local stack ? */
+ if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
+ /* yes */
+ new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
+ } else {
+ /* no */
+ if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset)))
+ == NULLSEQORSET) {
+ return (-1);
+ }
+ }
+ ber->ber_sos_stack_posn++;
+
+ if (ber->ber_sos == NULLSEQORSET)
+ new_sos->sos_first = ber->ber_ptr;
+ else
+ new_sos->sos_first = ber->ber_sos->sos_ptr;
+
+ /* Set aside room for a 4 byte length field */
+ new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) +
+ FOUR_BYTE_LEN;
+ new_sos->sos_tag = tag;
+
+ new_sos->sos_next = ber->ber_sos;
+ new_sos->sos_clen = 0;
+
+ ber->ber_sos = new_sos;
+ if (ber->ber_sos->sos_ptr > ber->ber_end) {
+ (void) realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end);
+ }
+ return (0);
+}
+
+static int
+kmfber_start_seq(BerElement *ber, ber_tag_t tag)
+{
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_CONSTRUCTED_SEQUENCE;
+
+ return (ber_start_seqorset(ber, tag));
+}
+
+static int
+kmfber_start_set(BerElement *ber, ber_tag_t tag)
+{
+ if (tag == KMFBER_DEFAULT)
+ tag = BER_CONSTRUCTED_SET;
+
+ return (ber_start_seqorset(ber, tag));
+}
+
+static int
+ber_put_seqorset(BerElement *ber)
+{
+ ber_int_t netlen, len, taglen, lenlen;
+ unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1;
+ Seqorset *next;
+ Seqorset **sos = &ber->ber_sos;
+
+ /*
+ * If this is the toplevel sequence or set, we need to actually
+ * write the stuff out. Otherwise, it's already been put in
+ * the appropriate buffer and will be written when the toplevel
+ * one is written. In this case all we need to do is update the
+ * length and tag.
+ */
+
+ len = (*sos)->sos_clen;
+ netlen = (ber_len_t)htonl(len);
+
+ if (ber->ber_options & KMFBER_OPT_USE_DER) {
+ lenlen = kmfber_calc_lenlen(len);
+ } else {
+ lenlen = FOUR_BYTE_LEN;
+ }
+
+ if ((next = (*sos)->sos_next) == NULLSEQORSET) {
+ /* write the tag */
+ if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1)
+ return (-1);
+
+ if (ber->ber_options & KMFBER_OPT_USE_DER) {
+ /* Write the length in the minimum # of octets */
+ if (kmfber_put_len(ber, len, 1) == -1)
+ return (-1);
+
+ if (lenlen != FOUR_BYTE_LEN) {
+ /*
+ * We set aside FOUR_BYTE_LEN bytes for
+ * the length field. Move the data if
+ * we don't actually need that much
+ */
+ (void) memmove((*sos)->sos_first + taglen +
+ lenlen, (*sos)->sos_first + taglen +
+ FOUR_BYTE_LEN, len);
+ }
+ } else {
+ /* Fill FOUR_BYTE_LEN bytes for length field */
+ /* one byte of length length */
+ if (kmfber_write(ber, (char *)&ltag, 1, 1) != 1)
+ return (-1);
+
+ /* the length itself */
+ if (kmfber_write(ber,
+ (char *)&netlen + sizeof (ber_int_t)
+ - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) !=
+ FOUR_BYTE_LEN - 1)
+ return (-1);
+ }
+ /* The ber_ptr is at the set/seq start - move it to the end */
+ ber->ber_ptr += len;
+ } else {
+ ber_tag_t ntag;
+
+ /* the tag */
+ taglen = kmfber_calc_taglen((*sos)->sos_tag);
+ ntag = htonl((*sos)->sos_tag);
+ (void) memmove((*sos)->sos_first, (char *)&ntag +
+ sizeof (ber_int_t) - taglen, taglen);
+
+ if (ber->ber_options & KMFBER_OPT_USE_DER) {
+ ltag = (lenlen == 1) ? (unsigned char)len :
+ (unsigned char) (0x80 + (lenlen - 1));
+ }
+
+ /* one byte of length length */
+ (void) memmove((*sos)->sos_first + 1, &ltag, 1);
+
+ if (ber->ber_options & KMFBER_OPT_USE_DER) {
+ if (lenlen > 1) {
+ /* Write the length itself */
+ (void) memmove((*sos)->sos_first + 2,
+ (char *)&netlen + sizeof (ber_uint_t) -
+ (lenlen - 1),
+ lenlen - 1);
+ }
+ if (lenlen != FOUR_BYTE_LEN) {
+ /*
+ * We set aside FOUR_BYTE_LEN bytes for
+ * the length field. Move the data if
+ * we don't actually need that much
+ */
+ (void) memmove((*sos)->sos_first + taglen +
+ lenlen, (*sos)->sos_first + taglen +
+ FOUR_BYTE_LEN, len);
+ }
+ } else {
+ /* the length itself */
+ (void) memmove((*sos)->sos_first + taglen + 1,
+ (char *) &netlen + sizeof (ber_int_t) -
+ (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1);
+ }
+
+ next->sos_clen += (taglen + lenlen + len);
+ next->sos_ptr += (taglen + lenlen + len);
+ }
+
+ /* we're done with this seqorset, so free it up */
+ /* was this one from the local stack ? */
+ if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) {
+ free((char *)(*sos));
+ }
+ ber->ber_sos_stack_posn--;
+ *sos = next;
+
+ return (taglen + lenlen + len);
+}
+
+/* VARARGS */
+int
+kmfber_printf(BerElement *ber, const char *fmt, ...)
+{
+ va_list ap;
+ char *s, **ss;
+ struct berval **bv, *oid;
+ int rc, i, t;
+ ber_int_t len;
+
+ va_start(ap, fmt);
+
+#ifdef KMFBER_DEBUG
+ if (lber_debug & 64) {
+ char msg[80];
+ sprintf(msg, "kmfber_printf fmt (%s)\n", fmt);
+ ber_err_print(msg);
+ }
+#endif
+
+ for (rc = 0; *fmt && rc != -1; fmt++) {
+ switch (*fmt) {
+ case 'b': /* boolean */
+ i = va_arg(ap, int);
+ rc = kmfber_put_boolean(ber, i, ber->ber_tag);
+ break;
+
+ case 'i': /* int */
+ i = va_arg(ap, int);
+ rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag);
+ break;
+
+ case 'D': /* Object ID */
+ if ((oid = va_arg(ap, struct berval *)) == NULL)
+ break;
+ rc = ber_put_oid(ber, oid, ber->ber_tag);
+ break;
+ case 'I': /* int */
+ s = va_arg(ap, char *);
+ len = va_arg(ap, ber_int_t);
+ rc = ber_put_big_int(ber, ber->ber_tag, s, len);
+ break;
+
+ case 'e': /* enumeration */
+ i = va_arg(ap, int);
+ rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag);
+ break;
+
+ case 'l':
+ t = va_arg(ap, int);
+ rc = kmfber_put_len(ber, t, 0);
+ break;
+ case 'n': /* null */
+ rc = kmfber_put_null(ber, ber->ber_tag);
+ break;
+
+ case 'o': /* octet string (non-null terminated) */
+ s = va_arg(ap, char *);
+ len = va_arg(ap, int);
+ rc = kmfber_put_ostring(ber, s, len, ber->ber_tag);
+ break;
+
+ case 's': /* string */
+ s = va_arg(ap, char *);
+ rc = kmfber_put_string(ber, s, ber->ber_tag);
+ break;
+
+ case 'B': /* bit string */
+ s = va_arg(ap, char *);
+ len = va_arg(ap, int); /* in bits */
+ rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag);
+ break;
+
+ case 't': /* tag for the next element */
+ ber->ber_tag = va_arg(ap, ber_tag_t);
+ ber->ber_usertag = 1;
+ break;
+
+ case 'T': /* Write an explicit tag, but don't change current */
+ t = va_arg(ap, int);
+ rc = ber_put_tag(ber, t, 0);
+ break;
+
+ case 'v': /* vector of strings */
+ if ((ss = va_arg(ap, char **)) == NULL)
+ break;
+ for (i = 0; ss[i] != NULL; i++) {
+ if ((rc = kmfber_put_string(ber, ss[i],
+ ber->ber_tag)) == -1)
+ break;
+ }
+ break;
+
+ case 'V': /* sequences of strings + lengths */
+ if ((bv = va_arg(ap, struct berval **)) == NULL)
+ break;
+ for (i = 0; bv[i] != NULL; i++) {
+ if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val,
+ bv[i]->bv_len, ber->ber_tag)) == -1)
+ break;
+ }
+ break;
+
+ case '{': /* begin sequence */
+ rc = kmfber_start_seq(ber, ber->ber_tag);
+ break;
+
+ case '}': /* end sequence */
+ rc = ber_put_seqorset(ber);
+ break;
+
+ case '[': /* begin set */
+ rc = kmfber_start_set(ber, ber->ber_tag);
+ break;
+
+ case ']': /* end set */
+ rc = ber_put_seqorset(ber);
+ break;
+
+ default: {
+#ifdef KMFBER_DEBUG
+ char msg[80];
+ sprintf(msg, "unknown fmt %c\n", *fmt);
+ ber_err_print(msg);
+#endif
+ rc = -1;
+ break;
+ }
+ }
+
+ if (ber->ber_usertag == 0)
+ ber->ber_tag = KMFBER_DEFAULT;
+ else
+ ber->ber_usertag = 0;
+ }
+
+ va_end(ap);
+
+ return (rc);
+}