X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Fliblber%2Fencode.c;h=4bbeb00d8c26601eeb015c9945128caf3a12d416;hb=e3defe9075835ccd62258843c1b95678714476b1;hp=f036dfeef46690acdca602bf21e668148de449cd;hpb=702109706b76fea49bde29d6351b2aaa4ee0e7fa;p=openldap diff --git a/libraries/liblber/encode.c b/libraries/liblber/encode.c index f036dfeef4..4bbeb00d8c 100644 --- a/libraries/liblber/encode.c +++ b/libraries/liblber/encode.c @@ -1,6 +1,19 @@ /* encode.c - ber output encoding routines */ -/* - * Copyright (c) 1990 Regents of the University of Michigan. +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 1998-2012 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. * All rights reserved. * * Redistribution and use in source and binary forms are permitted @@ -10,544 +23,526 @@ * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ #include "portable.h" +#include +#include #include -#include -#ifdef STDC_HEADERS -#include -#else -#include -#endif +#include +#include #include #include #include "lber-int.h" -static int ber_put_len LDAP_P(( BerElement *ber, - unsigned long len, int nosos )); - -static int ber_start_seqorset LDAP_P(( BerElement *ber, - unsigned long tag )); - -static int ber_put_seqorset LDAP_P(( BerElement *ber )); -static int ber_put_int_or_enum LDAP_P(( BerElement *ber, - long num, unsigned long tag )); +#define OCTET_SIZE(type) ((ber_len_t) (sizeof(type)*CHAR_BIT + 7) / 8) +#define TAGBUF_SIZE OCTET_SIZE(ber_tag_t) +#define LENBUF_SIZE (1 + OCTET_SIZE(ber_len_t)) +#define HEADER_SIZE (TAGBUF_SIZE + LENBUF_SIZE) +/* + * BER element size constrains: + * + * - We traditionally support a length of max 0xffffffff. However + * some functions return an int length so that is their max. + * MAXINT_BERSIZE is the max for those functions. + * + * - MAXINT_BERSIZE must fit in MAXINT_BERSIZE_OCTETS octets. + * + * - sizeof(ber_elem_size_t) is normally MAXINT_BERSIZE_OCTETS: + * Big enough for MAXINT_BERSIZE, but not more. (Larger wastes + * space in the working encoding and DER encoding of a sequence + * or set. Smaller further limits sizes near a sequence/set.) + * + * ber_len_t is mostly unrelated to this. Which may be for the best, + * since it is also used for lengths of data that are never encoded. + */ +#define MAXINT_BERSIZE \ + (INT_MAX>0xffffffffUL ? (ber_len_t) 0xffffffffUL : INT_MAX-HEADER_SIZE) +#define MAXINT_BERSIZE_OCTETS 4 +typedef ber_uint_t ber_elem_size_t; /* normally 32 bits */ -static int -ber_calc_taglen( unsigned long tag ) -{ - int i; - long mask; - - /* find the first non-all-zero byte in the tag */ - for ( i = sizeof(long) - 1; i > 0; i-- ) { - mask = (0xffL << (i * 8)); - /* not all zero */ - if ( tag & mask ) - break; - } - - return( i + 1 ); -} -static int -ber_put_tag( BerElement *ber, unsigned long tag, int nosos ) +/* Prepend tag to ptr, which points to the end of a tag buffer */ +static unsigned char * +ber_prepend_tag( unsigned char *ptr, ber_tag_t tag ) { - int taglen; - unsigned long ntag; + do { + *--ptr = (unsigned char) tag & 0xffU; + } while ( (tag >>= 8) != 0 ); - taglen = ber_calc_taglen( tag ); - - ntag = AC_HTONL( tag ); - - return( ber_write( ber, ((char *) &ntag) + sizeof(long) - taglen, - taglen, nosos ) ); + return ptr; } -static int -ber_calc_lenlen( unsigned long len ) +/* Prepend ber length to ptr, which points to the end of a length buffer */ +static unsigned char * +ber_prepend_len( unsigned char *ptr, ber_len_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 <= 0xFF ) - return( 2 ); - if ( len <= 0xFFFFL ) - return( 3 ); - if ( len <= 0xFFFFFFL ) - return( 4 ); + *--ptr = (unsigned char) len & 0xffU; + + if ( len >= 0x80 ) { + unsigned char *endptr = ptr--; - return( 5 ); + while ( (len >>= 8) != 0 ) { + *ptr-- = (unsigned char) len & 0xffU; + } + *ptr = (unsigned char) (endptr - ptr) + 0x80U; + } + + return ptr; } -static int -ber_put_len( BerElement *ber, unsigned long len, int nosos ) +/* out->bv_len should be the buffer size on input */ +int +ber_encode_oid( BerValue *in, BerValue *out ) { - int i; - char lenlen; - long mask; - unsigned long netlen; + unsigned char *der; + unsigned long val1, val; + int i, j, len; + char *ptr, *end, *inend; + + assert( in != NULL ); + assert( out != NULL ); + + if ( !out->bv_val || out->bv_len < in->bv_len/2 ) + return -1; + + der = (unsigned char *) out->bv_val; + ptr = in->bv_val; + inend = ptr + in->bv_len; + + /* OIDs start with <0-1>.<0-39> or 2., DER-encoded 40*val1+val2 */ + if ( !isdigit( (unsigned char) *ptr )) return -1; + val1 = strtoul( ptr, &end, 10 ); + if ( end == ptr || val1 > 2 ) return -1; + if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1; + val = strtoul( end, &ptr, 10 ); + if ( ptr == end ) return -1; + if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1; + val += val1 * 40; + + for (;;) { + if ( ptr > inend ) return -1; + + /* Write the OID component little-endian, then reverse it */ + len = 0; + do { + der[len++] = (val & 0xff) | 0x80; + } while ( (val >>= 7) != 0 ); + der[0] &= 0x7f; + for ( i = 0, j = len; i < --j; i++ ) { + unsigned char tmp = der[i]; + der[i] = der[j]; + der[j] = tmp; + } + der += len; - /* - * short len if it's less than 128 - one byte giving the len, - * with bit 8 0. - */ + if ( ptr == inend ) + break; - if ( len <= 127 ) { - netlen = AC_HTONL( len ); - return( ber_write( ber, (char *) &netlen + sizeof(long) - 1, - 1, nosos ) ); + if ( *ptr++ != '.' ) return -1; + if ( !isdigit( (unsigned char) *ptr )) return -1; + val = strtoul( ptr, &end, 10 ); + if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1; + ptr = end; } - /* - * 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(long) - 1; i > 0; i-- ) { - mask = (0xffL << (i * 8)); - /* not all zero */ - if ( len & mask ) - break; - } - lenlen = (unsigned char) ++i; - if ( lenlen > 4 ) - return( -1 ); - lenlen |= 0x80; - - /* write the length of the length */ - if ( ber_write( ber, &lenlen, 1, nosos ) != 1 ) - return( -1 ); - - /* write the length itself */ - netlen = AC_HTONL( len ); - if ( ber_write( ber, (char *) &netlen + (sizeof(long) - i), i, nosos ) - != i ) - return( -1 ); - - return( i + 1 ); + out->bv_len = (char *)der - out->bv_val; + return 0; } static int -ber_put_int_or_enum( BerElement *ber, long num, unsigned long tag ) +ber_put_int_or_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) { - int i, sign, taglen; - int len, lenlen; - long 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(long) - 1; i > 0; i-- ) { - mask = (0xffL << (i * 8)); - - if ( sign ) { - /* not all ones */ - if ( (num & mask) != mask ) - break; - } else { - /* not all zero */ - if ( num & mask ) - break; - } + ber_uint_t unum; + unsigned char sign, data[TAGBUF_SIZE+1 + OCTET_SIZE(ber_int_t)], *ptr; + + sign = 0; + unum = num; /* Bit fiddling should be done with unsigned values */ + if ( num < 0 ) { + sign = 0xffU; + unum = ~unum; + } + for ( ptr = &data[sizeof(data) - 1] ;; unum >>= 8 ) { + *ptr-- = (sign ^ (unsigned char) unum) & 0xffU; + if ( unum < 0x80 ) /* top bit at *ptr is sign bit */ + 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 & (0x80L << (i * 8))); - if ( (mask && !sign) || (sign && !mask) ) - i++; - - len = i + 1; - - if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) - return( -1 ); - - if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ) - return( -1 ); - i++; - netnum = AC_HTONL( num ); - if ( ber_write( ber, (char *) &netnum + (sizeof(long) - i), i, 0 ) - != i ) - return( -1 ); + *ptr = (unsigned char) (&data[sizeof(data) - 1] - ptr); /* length */ + ptr = ber_prepend_tag( ptr, tag ); - /* length of tag + length + contents */ - return( taglen + lenlen + i ); + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); } int -ber_put_enum( BerElement *ber, long num, unsigned long tag ) +ber_put_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) { - if ( tag == LBER_DEFAULT ) + if ( tag == LBER_DEFAULT ) { tag = LBER_ENUMERATED; + } - return( ber_put_int_or_enum( ber, num, tag ) ); + return ber_put_int_or_enum( ber, num, tag ); } int -ber_put_int( BerElement *ber, long num, unsigned long tag ) +ber_put_int( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) { - if ( tag == LBER_DEFAULT ) + if ( tag == LBER_DEFAULT ) { tag = LBER_INTEGER; + } - return( ber_put_int_or_enum( ber, num, tag ) ); + return ber_put_int_or_enum( ber, num, tag ); } int -ber_put_ostring( BerElement *ber, char *str, unsigned long len, - unsigned long tag ) +ber_put_ostring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t len, + ber_tag_t tag ) { - int taglen, lenlen, rc; -#ifdef STR_TRANSLATION - int free_str; -#endif /* STR_TRANSLATION */ + int rc; + unsigned char header[HEADER_SIZE], *ptr; - if ( tag == LBER_DEFAULT ) + if ( tag == LBER_DEFAULT ) { tag = LBER_OCTETSTRING; + } - if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) - return( -1 ); - -#ifdef STR_TRANSLATION - if ( len > 0 && ( ber->ber_options & LBER_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; + if ( len > MAXINT_BERSIZE ) { + return -1; } -#endif /* STR_TRANSLATION */ - if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 || - (unsigned long) ber_write( ber, str, len, 0 ) != len ) { - rc = -1; - } else { - /* return length of tag + length + contents */ - rc = taglen + lenlen + len; + ptr = ber_prepend_len( &header[sizeof(header)], len ); + ptr = ber_prepend_tag( ptr, tag ); + + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + contents) */ + return rc + (int) len; } -#ifdef STR_TRANSLATION - if ( free_str ) { - free( str ); + return -1; +} + +int +ber_put_berval( + BerElement *ber, + struct berval *bv, + ber_tag_t tag ) +{ + if( bv == NULL || bv->bv_len == 0 ) { + return ber_put_ostring( ber, "", (ber_len_t) 0, tag ); } -#endif /* STR_TRANSLATION */ - return( rc ); + return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag ); } int -ber_put_string( BerElement *ber, char *str, unsigned long tag ) +ber_put_string( + BerElement *ber, + LDAP_CONST char *str, + ber_tag_t tag ) { - return( ber_put_ostring( ber, str, strlen( str ), tag )); + assert( str != NULL ); + + return ber_put_ostring( ber, str, strlen( str ), tag ); } int -ber_put_bitstring( BerElement *ber, char *str, - unsigned long blen /* in bits */, unsigned long tag ) +ber_put_bitstring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t blen /* in bits */, + ber_tag_t tag ) { - int taglen, lenlen, len; - unsigned char unusedbits; + int rc; + ber_len_t len; + unsigned char unusedbits, header[HEADER_SIZE + 1], *ptr; - if ( tag == LBER_DEFAULT ) + if ( tag == LBER_DEFAULT ) { tag = LBER_BITSTRING; + } - if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) - return( -1 ); - - len = ( blen + 7 ) / 8; - unusedbits = (unsigned char) ((len * 8) - blen); - if ( (lenlen = ber_put_len( ber, len + 1, 0 )) == -1 ) - return( -1 ); + unusedbits = (unsigned char) -blen & 7; + len = blen / 8 + (unusedbits != 0); /* (blen+7)/8 without overflow */ + if ( len >= MAXINT_BERSIZE ) { + return -1; + } - if ( ber_write( ber, (char *)&unusedbits, 1, 0 ) != 1 ) - return( -1 ); + header[sizeof(header) - 1] = unusedbits; + ptr = ber_prepend_len( &header[sizeof(header) - 1], len + 1 ); + ptr = ber_prepend_tag( ptr, tag ); - if ( ber_write( ber, str, len, 0 ) != len ) - return( -1 ); + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + unused bit count + bitstring) */ + return rc + (int) len; + } - /* return length of tag + length + unused bit count + contents */ - return( taglen + 1 + lenlen + len ); + return -1; } int -ber_put_null( BerElement *ber, unsigned long tag ) +ber_put_null( BerElement *ber, ber_tag_t tag ) { - int taglen; + unsigned char data[TAGBUF_SIZE + 1], *ptr; - if ( tag == LBER_DEFAULT ) + if ( tag == LBER_DEFAULT ) { tag = LBER_NULL; + } - if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) - return( -1 ); - - if ( ber_put_len( ber, 0, 0 ) != 1 ) - return( -1 ); + data[sizeof(data) - 1] = 0; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 1], tag ); - return( taglen + 1 ); + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); } int -ber_put_boolean( BerElement *ber, int boolval, unsigned long tag ) +ber_put_boolean( + BerElement *ber, + ber_int_t boolval, + ber_tag_t tag ) { - int taglen; - unsigned char trueval = 0xff; - unsigned char falseval = 0x00; + unsigned char data[TAGBUF_SIZE + 2], *ptr; if ( tag == LBER_DEFAULT ) tag = LBER_BOOLEAN; - if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) - return( -1 ); + data[sizeof(data) - 1] = boolval ? 0xff : 0; + data[sizeof(data) - 2] = 1; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 2], tag ); - if ( ber_put_len( ber, 1, 0 ) != 1 ) - return( -1 ); + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} - if ( ber_write( ber, (char *)(boolval ? &trueval : &falseval), 1, 0 ) - != 1 ) - return( -1 ); - return( taglen + 2 ); -} +/* Max number of length octets in a sequence or set, normally 5 */ +#define SOS_LENLEN (1 + (sizeof(ber_elem_size_t) > MAXINT_BERSIZE_OCTETS ? \ + (ber_len_t) sizeof(ber_elem_size_t) : MAXINT_BERSIZE_OCTETS)) -#define FOUR_BYTE_LEN 5 +/* Header of incomplete sequence or set */ +typedef struct seqorset_header { + char xtagbuf[TAGBUF_SIZE + 1]; /* room for tag + len(tag or len) */ + union { + ber_elem_size_t offset; /* enclosing seqence/set */ + char padding[SOS_LENLEN-1]; /* for final length encoding */ + } next_sos; +# define SOS_TAG_END(header) ((unsigned char *) &(header).next_sos - 1) +} Seqorset_header; +/* Start a sequence or set */ static int -ber_start_seqorset( BerElement *ber, unsigned long tag ) +ber_start_seqorset( + BerElement *ber, + ber_tag_t tag ) { - Seqorset *new; + /* + * Write the tag and SOS_LENLEN octets reserved for length, to ber. + * For now, length octets = (tag length, previous ber_sos_inner). + * + * Update ber_sos_inner and the write-cursor ber_sos_ptr. ber_ptr + * will not move until the outermost sequence or set is complete. + */ - if ( (new = (Seqorset *) calloc( sizeof(Seqorset), 1 )) - == NULLSEQORSET ) - return( -1 ); - new->sos_ber = ber; - if ( ber->ber_sos == NULLSEQORSET ) - new->sos_first = ber->ber_ptr; - else - new->sos_first = ber->ber_sos->sos_ptr; + Seqorset_header header; + unsigned char *headptr; + ber_len_t taglen, headlen; + char *dest, **p; - /* Set aside room for a 4 byte length field */ - new->sos_ptr = new->sos_first + ber_calc_taglen( tag ) + FOUR_BYTE_LEN; - new->sos_tag = tag; + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); - new->sos_next = ber->ber_sos; - ber->ber_sos = new; + if ( ber->ber_sos_ptr == NULL ) { /* outermost sequence/set? */ + header.next_sos.offset = 0; + p = &ber->ber_ptr; + } else { + if ( (ber_len_t) -1 > (ber_elem_size_t) -1 ) { + if ( ber->ber_sos_inner > (ber_elem_size_t) -1 ) + return -1; + } + header.next_sos.offset = ber->ber_sos_inner; + p = &ber->ber_sos_ptr; + } + headptr = ber_prepend_tag( SOS_TAG_END(header), tag ); + *SOS_TAG_END(header) = taglen = SOS_TAG_END(header) - headptr; + headlen = taglen + SOS_LENLEN; + + /* As ber_write(,headptr,headlen,) except update ber_sos_ptr, not *p */ + if ( headlen > (ber_len_t) (ber->ber_end - *p) ) { + if ( ber_realloc( ber, headlen ) != 0 ) + return -1; + } + dest = *p; + AC_MEMCPY( dest, headptr, headlen ); + ber->ber_sos_ptr = dest + headlen; + + ber->ber_sos_inner = dest + taglen - ber->ber_buf; - return( 0 ); + /* + * Do not return taglen + SOS_LENLEN here - then ber_put_seqorset() + * should return lenlen - SOS_LENLEN + len, which can be < 0. + */ + return 0; } int -ber_start_seq( BerElement *ber, unsigned long tag ) +ber_start_seq( BerElement *ber, ber_tag_t tag ) { - if ( tag == LBER_DEFAULT ) + if ( tag == LBER_DEFAULT ) { tag = LBER_SEQUENCE; + } - return( ber_start_seqorset( ber, tag ) ); + return ber_start_seqorset( ber, tag ); } int -ber_start_set( BerElement *ber, unsigned long tag ) +ber_start_set( BerElement *ber, ber_tag_t tag ) { - if ( tag == LBER_DEFAULT ) + if ( tag == LBER_DEFAULT ) { tag = LBER_SET; + } - return( ber_start_seqorset( ber, tag ) ); + return ber_start_seqorset( ber, tag ); } +/* End a sequence or set */ static int ber_put_seqorset( BerElement *ber ) { - unsigned long len, netlen; - int taglen, lenlen; - unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1; - Seqorset *next; - Seqorset **sos = &ber->ber_sos; + Seqorset_header header; + unsigned char *lenptr; /* length octets in the sequence/set */ + ber_len_t len; /* length(contents) */ + ber_len_t xlen; /* len + length(length) */ - /* - * 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. - */ + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); - len = (*sos)->sos_clen; - netlen = AC_HTONL( len ); - if ( sizeof(long) > 4 && len > 0xFFFFFFFFL ) - return( -1 ); + if ( ber->ber_sos_ptr == NULL ) return -1; - if ( ber->ber_options & LBER_USE_DER ) { - lenlen = ber_calc_lenlen( len ); - } else { - lenlen = FOUR_BYTE_LEN; + lenptr = (unsigned char *) ber->ber_buf + ber->ber_sos_inner; + xlen = ber->ber_sos_ptr - (char *) lenptr; + if ( xlen > MAXINT_BERSIZE + SOS_LENLEN ) { + return -1; } - 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 & LBER_USE_DER ) { - /* Write the length in the minimum # of octets */ - if ( ber_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 - */ - SAFEMEMCPY( (*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 ( ber_write( ber, (char *)<ag, 1, 1 ) != 1 ) - return( -1 ); - - /* the length itself */ - if ( ber_write( ber, (char *) &netlen + sizeof(long) - - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1 ) - != FOUR_BYTE_LEN - 1 ) - return( -1 ); + /* Extract sequence/set information from length octets */ + memcpy( SOS_TAG_END(header), lenptr, SOS_LENLEN ); + + /* Store length, and close gap of leftover reserved length octets */ + len = xlen - SOS_LENLEN; + if ( !(ber->ber_options & LBER_USE_DER) ) { + int i; + lenptr[0] = SOS_LENLEN - 1 + 0x80; /* length(length)-1 */ + for( i = SOS_LENLEN; --i > 0; len >>= 8 ) { + lenptr[i] = len & 0xffU; } - /* The ber_ptr is at the set/seq start - move it to the end */ - (*sos)->sos_ber->ber_ptr += len; } else { - unsigned long ntag; - - /* the tag */ - taglen = ber_calc_taglen( (*sos)->sos_tag ); - ntag = AC_HTONL( (*sos)->sos_tag ); - SAFEMEMCPY( (*sos)->sos_first, (char *) &ntag + - sizeof(long) - taglen, taglen ); - - if ( ber->ber_options & LBER_USE_DER ) { - ltag = (lenlen == 1) - ? (unsigned char) len - : 0x80 + (lenlen - 1); + unsigned char *p = ber_prepend_len( lenptr + SOS_LENLEN, len ); + ber_len_t unused = p - lenptr; + if ( unused != 0 ) { + /* length(length) < the reserved SOS_LENLEN bytes */ + xlen -= unused; + AC_MEMCPY( lenptr, p, xlen ); + ber->ber_sos_ptr = (char *) lenptr + xlen; } - - /* one byte of length length */ - SAFEMEMCPY( (*sos)->sos_first + 1, <ag, 1 ); - - if ( ber->ber_options & LBER_USE_DER ) { - if (lenlen > 1) { - /* Write the length itself */ - SAFEMEMCPY( (*sos)->sos_first + 2, - (char *)&netlen + sizeof(unsigned long) - - (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 - */ - SAFEMEMCPY( (*sos)->sos_first + taglen + - lenlen, (*sos)->sos_first + taglen + - FOUR_BYTE_LEN, len ); - } - } else { - /* the length itself */ - SAFEMEMCPY( (*sos)->sos_first + taglen + 1, - (char *) &netlen + sizeof(long) - - (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 */ - free( (char *) (*sos) ); - *sos = next; + ber->ber_sos_inner = header.next_sos.offset; + if ( header.next_sos.offset == 0 ) { /* outermost sequence/set? */ + /* The ber_ptr is at the set/seq start - move it to the end */ + ber->ber_ptr = ber->ber_sos_ptr; + ber->ber_sos_ptr = NULL; + } - return( taglen + lenlen + len ); + return xlen + *SOS_TAG_END(header); /* lenlen + len + taglen */ } int ber_put_seq( BerElement *ber ) { - return( ber_put_seqorset( ber ) ); + return ber_put_seqorset( ber ); } int ber_put_set( BerElement *ber ) { - return( ber_put_seqorset( ber ) ); + return ber_put_seqorset( ber ); } +/* N tag */ +static ber_tag_t lber_int_null = 0; + /* VARARGS */ int -ber_printf -#ifdef STDC_HEADERS - ( BerElement *ber, char *fmt, ... ) -#else - ( va_alist ) -va_dcl -#endif +ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... ) { va_list ap; -#ifndef STDC_HEADERS - BerElement *ber; - char *fmt; -#endif char *s, **ss; - struct berval **bv; - int rc, i; - unsigned long len; + struct berval *bv, **bvp; + int rc; + ber_int_t i; + ber_len_t len; + + assert( ber != NULL ); + assert( fmt != NULL ); + assert( LBER_VALID( ber ) ); -#ifdef STDC_HEADERS va_start( ap, fmt ); -#else - va_start( ap ); - ber = va_arg( ap, BerElement * ); - fmt = va_arg( ap, char * ); -#endif for ( rc = 0; *fmt && rc != -1; fmt++ ) { switch ( *fmt ) { + case '!': { /* hook */ + BEREncodeCallback *f; + void *p; + + ber->ber_usertag = 0; + + f = va_arg( ap, BEREncodeCallback * ); + p = va_arg( ap, void * ); + rc = (*f)( ber, p ); + + if ( ber->ber_usertag ) { + goto next; + } + } break; + case 'b': /* boolean */ - i = va_arg( ap, int ); + i = va_arg( ap, ber_int_t ); rc = ber_put_boolean( ber, i, ber->ber_tag ); break; case 'i': /* int */ - i = va_arg( ap, int ); + i = va_arg( ap, ber_int_t ); rc = ber_put_int( ber, i, ber->ber_tag ); break; case 'e': /* enumeration */ - i = va_arg( ap, int ); + i = va_arg( ap, ber_int_t ); rc = ber_put_enum( ber, i, ber->ber_tag ); break; @@ -555,27 +550,41 @@ va_dcl rc = ber_put_null( ber, ber->ber_tag ); break; + case 'N': /* Debug NULL */ + rc = 0; + if( lber_int_null != 0 ) { + /* Insert NULL to ensure peer ignores unknown tags */ + rc = ber_put_null( ber, lber_int_null ); + } + break; + case 'o': /* octet string (non-null terminated) */ s = va_arg( ap, char * ); - len = va_arg( ap, int ); + len = va_arg( ap, ber_len_t ); rc = ber_put_ostring( ber, s, len, ber->ber_tag ); break; + case 'O': /* berval octet string */ + bv = va_arg( ap, struct berval * ); + if( bv == NULL ) break; + rc = ber_put_berval( ber, bv, ber->ber_tag ); + break; + case 's': /* string */ s = va_arg( ap, char * ); rc = ber_put_string( ber, s, ber->ber_tag ); break; case 'B': /* bit string */ + case 'X': /* bit string (deprecated) */ s = va_arg( ap, char * ); - len = va_arg( ap, int ); /* in bits */ + len = va_arg( ap, ber_len_t ); /* in bits */ rc = ber_put_bitstring( ber, s, len, ber->ber_tag ); break; case 't': /* tag for the next element */ - ber->ber_tag = va_arg( ap, unsigned long ); - ber->ber_usertag = 1; - break; + ber->ber_tag = va_arg( ap, ber_tag_t ); + goto next; case 'v': /* vector of strings */ if ( (ss = va_arg( ap, char ** )) == NULL ) @@ -588,11 +597,21 @@ va_dcl break; case 'V': /* sequences of strings + lengths */ - if ( (bv = va_arg( ap, struct berval ** )) == NULL ) + if ( (bvp = va_arg( ap, struct berval ** )) == NULL ) break; - for ( i = 0; bv[i] != NULL; i++ ) { - if ( (rc = ber_put_ostring( ber, bv[i]->bv_val, - bv[i]->bv_len, ber->ber_tag )) == -1 ) + for ( i = 0; bvp[i] != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, bvp[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'W': /* BerVarray */ + if ( (bv = va_arg( ap, BerVarray )) == NULL ) + break; + for ( i = 0; bv[i].bv_val != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, &bv[i], + ber->ber_tag )) == -1 ) break; } break; @@ -614,20 +633,19 @@ va_dcl break; default: -#ifdef LDAP_LIBUI - fprintf( stderr, "unknown fmt %c\n", *fmt ); -#endif /* LDAP_LIBUI */ + if( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, + "ber_printf: unknown fmt %c\n", *fmt ); + } rc = -1; break; } - if ( ber->ber_usertag == 0 ) - ber->ber_tag = LBER_DEFAULT; - else - ber->ber_usertag = 0; + ber->ber_tag = LBER_DEFAULT; + next:; } va_end( ap ); - return( rc ); + return rc; }