/usr/src/lib/libkmf/ber_der/common/encode.c
C | 737 lines | 491 code | 125 blank | 121 comment | 153 complexity | ce0c067dcf8cd6dbc1e99b86ac386c3b MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, GPL-2.0, GPL-3.0, 0BSD, BSD-2-Clause, BSD-3-Clause-No-Nuclear-License-2014, MPL-2.0-no-copyleft-exception, AGPL-1.0, LGPL-2.1, LGPL-2.0
- /*
- * -*- 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 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
- #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;
- char zero = 0x00;
- if (tag == KMFBER_DEFAULT)
- tag = BER_INTEGER;
- if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
- return (-1);
- /* Add a leading 0 if the high order bit is set */
- if (data[0] & 0x80)
- len++;
- ilen = (ber_int_t)len;
- if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1)
- return (-1);
- /* add leading 0 if hi bit set */
- if ((data[0] & 0x80) && kmfber_write(ber, &zero, 1, 0) != 1)
- return (-1);
- /* Adjust the length of the write if hi-order bit is set */
- if (data[0] & 0x80)
- ilen = len - 1;
- if (kmfber_write(ber, data, ilen, 0) != (ber_int_t)ilen) {
- return (-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) {
- if (kmfber_realloc(ber, ber->ber_sos->sos_ptr -
- ber->ber_end) != 0)
- return (-1);
- }
- 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 *)<ag, 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, <ag, 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);
- }