1/* $NetBSD: encode.c,v 1.1.1.3 2010/12/12 15:21:28 adam Exp $ */ 2 3/* encode.c - ber output encoding routines */ 4/* OpenLDAP: pkg/ldap/libraries/liblber/encode.c,v 1.64.2.9 2010/04/13 20:22:53 kurt Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2010 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18/* Portions Copyright (c) 1990 Regents of the University of Michigan. 19 * All rights reserved. 20 * 21 * Redistribution and use in source and binary forms are permitted 22 * provided that this notice is preserved and that due credit is given 23 * to the University of Michigan at Ann Arbor. The name of the University 24 * may not be used to endorse or promote products derived from this 25 * software without specific prior written permission. This software 26 * is provided ``as is'' without express or implied warranty. 27 */ 28/* ACKNOWLEDGEMENTS: 29 * This work was originally developed by the University of Michigan 30 * (as part of U-MICH LDAP). 31 */ 32 33#include "portable.h" 34 35#include <ctype.h> 36#include <limits.h> 37#include <stdio.h> 38 39#include <ac/stdlib.h> 40 41#include <ac/stdarg.h> 42#include <ac/socket.h> 43#include <ac/string.h> 44 45#include "lber-int.h" 46 47 48#define OCTET_SIZE(type) ((ber_len_t) (sizeof(type)*CHAR_BIT + 7) / 8) 49#define TAGBUF_SIZE OCTET_SIZE(ber_tag_t) 50#define LENBUF_SIZE (1 + OCTET_SIZE(ber_len_t)) 51#define HEADER_SIZE (TAGBUF_SIZE + LENBUF_SIZE) 52 53/* 54 * BER element size constrains: 55 * 56 * - We traditionally support a length of max 0xffffffff. However 57 * some functions return an int length so that is their max. 58 * MAXINT_BERSIZE is the max for those functions. 59 * 60 * - MAXINT_BERSIZE must fit in MAXINT_BERSIZE_OCTETS octets. 61 * 62 * - sizeof(ber_elem_size_t) is normally MAXINT_BERSIZE_OCTETS: 63 * Big enough for MAXINT_BERSIZE, but not more. (Larger wastes 64 * space in the working encoding and DER encoding of a sequence 65 * or set. Smaller further limits sizes near a sequence/set.) 66 * 67 * ber_len_t is mostly unrelated to this. Which may be for the best, 68 * since it is also used for lengths of data that are never encoded. 69 */ 70#define MAXINT_BERSIZE \ 71 (INT_MAX>0xffffffffUL ? (ber_len_t) 0xffffffffUL : INT_MAX-HEADER_SIZE) 72#define MAXINT_BERSIZE_OCTETS 4 73typedef ber_uint_t ber_elem_size_t; /* normally 32 bits */ 74 75 76/* Prepend tag to ptr, which points to the end of a tag buffer */ 77static unsigned char * 78ber_prepend_tag( unsigned char *ptr, ber_tag_t tag ) 79{ 80 do { 81 *--ptr = (unsigned char) tag & 0xffU; 82 } while ( (tag >>= 8) != 0 ); 83 84 return ptr; 85} 86 87/* Prepend ber length to ptr, which points to the end of a length buffer */ 88static unsigned char * 89ber_prepend_len( unsigned char *ptr, ber_len_t len ) 90{ 91 /* 92 * short len if it's less than 128 - one byte giving the len, 93 * with bit 8 0. 94 * long len otherwise - one byte with bit 8 set, giving the 95 * length of the length, followed by the length itself. 96 */ 97 98 *--ptr = (unsigned char) len & 0xffU; 99 100 if ( len >= 0x80 ) { 101 unsigned char *endptr = ptr--; 102 103 while ( (len >>= 8) != 0 ) { 104 *ptr-- = (unsigned char) len & 0xffU; 105 } 106 *ptr = (unsigned char) (endptr - ptr) + 0x80U; 107 } 108 109 return ptr; 110} 111 112/* out->bv_len should be the buffer size on input */ 113int 114ber_encode_oid( BerValue *in, BerValue *out ) 115{ 116 unsigned char *der; 117 unsigned long val1, val; 118 int i, j, len; 119 char *ptr, *end, *inend; 120 121 assert( in != NULL ); 122 assert( out != NULL ); 123 124 if ( !out->bv_val || out->bv_len < in->bv_len/2 ) 125 return -1; 126 127 der = (unsigned char *) out->bv_val; 128 ptr = in->bv_val; 129 inend = ptr + in->bv_len; 130 131 /* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */ 132 if ( !isdigit( (unsigned char) *ptr )) return -1; 133 val1 = strtoul( ptr, &end, 10 ); 134 if ( end == ptr || val1 > 2 ) return -1; 135 if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1; 136 val = strtoul( end, &ptr, 10 ); 137 if ( ptr == end ) return -1; 138 if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1; 139 val += val1 * 40; 140 141 for (;;) { 142 if ( ptr > inend ) return -1; 143 144 /* Write the OID component little-endian, then reverse it */ 145 len = 0; 146 do { 147 der[len++] = (val & 0xff) | 0x80; 148 } while ( (val >>= 7) != 0 ); 149 der[0] &= 0x7f; 150 for ( i = 0, j = len; i < --j; i++ ) { 151 unsigned char tmp = der[i]; 152 der[i] = der[j]; 153 der[j] = tmp; 154 } 155 der += len; 156 157 if ( ptr == inend ) 158 break; 159 160 if ( *ptr++ != '.' ) return -1; 161 if ( !isdigit( (unsigned char) *ptr )) return -1; 162 val = strtoul( ptr, &end, 10 ); 163 if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1; 164 ptr = end; 165 } 166 167 out->bv_len = (char *)der - out->bv_val; 168 return 0; 169} 170 171static int 172ber_put_int_or_enum( 173 BerElement *ber, 174 ber_int_t num, 175 ber_tag_t tag ) 176{ 177 ber_uint_t unum; 178 unsigned char sign, data[TAGBUF_SIZE+1 + OCTET_SIZE(ber_int_t)], *ptr; 179 180 sign = 0; 181 unum = num; /* Bit fiddling should be done with unsigned values */ 182 if ( num < 0 ) { 183 sign = 0xffU; 184 unum = ~unum; 185 } 186 for ( ptr = &data[sizeof(data) - 1] ;; unum >>= 8 ) { 187 *ptr-- = (sign ^ (unsigned char) unum) & 0xffU; 188 if ( unum < 0x80 ) /* top bit at *ptr is sign bit */ 189 break; 190 } 191 192 *ptr = (unsigned char) (&data[sizeof(data) - 1] - ptr); /* length */ 193 ptr = ber_prepend_tag( ptr, tag ); 194 195 return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); 196} 197 198int 199ber_put_enum( 200 BerElement *ber, 201 ber_int_t num, 202 ber_tag_t tag ) 203{ 204 if ( tag == LBER_DEFAULT ) { 205 tag = LBER_ENUMERATED; 206 } 207 208 return ber_put_int_or_enum( ber, num, tag ); 209} 210 211int 212ber_put_int( 213 BerElement *ber, 214 ber_int_t num, 215 ber_tag_t tag ) 216{ 217 if ( tag == LBER_DEFAULT ) { 218 tag = LBER_INTEGER; 219 } 220 221 return ber_put_int_or_enum( ber, num, tag ); 222} 223 224int 225ber_put_ostring( 226 BerElement *ber, 227 LDAP_CONST char *str, 228 ber_len_t len, 229 ber_tag_t tag ) 230{ 231 int rc; 232 unsigned char header[HEADER_SIZE], *ptr; 233 234 if ( tag == LBER_DEFAULT ) { 235 tag = LBER_OCTETSTRING; 236 } 237 238 if ( len > MAXINT_BERSIZE ) { 239 return -1; 240 } 241 242 ptr = ber_prepend_len( &header[sizeof(header)], len ); 243 ptr = ber_prepend_tag( ptr, tag ); 244 245 rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); 246 if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { 247 /* length(tag + length + contents) */ 248 return rc + (int) len; 249 } 250 251 return -1; 252} 253 254int 255ber_put_berval( 256 BerElement *ber, 257 struct berval *bv, 258 ber_tag_t tag ) 259{ 260 if( bv == NULL || bv->bv_len == 0 ) { 261 return ber_put_ostring( ber, "", (ber_len_t) 0, tag ); 262 } 263 264 return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag ); 265} 266 267int 268ber_put_string( 269 BerElement *ber, 270 LDAP_CONST char *str, 271 ber_tag_t tag ) 272{ 273 assert( str != NULL ); 274 275 return ber_put_ostring( ber, str, strlen( str ), tag ); 276} 277 278int 279ber_put_bitstring( 280 BerElement *ber, 281 LDAP_CONST char *str, 282 ber_len_t blen /* in bits */, 283 ber_tag_t tag ) 284{ 285 int rc; 286 ber_len_t len; 287 unsigned char unusedbits, header[HEADER_SIZE + 1], *ptr; 288 289 if ( tag == LBER_DEFAULT ) { 290 tag = LBER_BITSTRING; 291 } 292 293 unusedbits = (unsigned char) -blen & 7; 294 len = blen / 8 + (unusedbits != 0); /* (blen+7)/8 without overflow */ 295 if ( len >= MAXINT_BERSIZE ) { 296 return -1; 297 } 298 299 header[sizeof(header) - 1] = unusedbits; 300 ptr = ber_prepend_len( &header[sizeof(header) - 1], len + 1 ); 301 ptr = ber_prepend_tag( ptr, tag ); 302 303 rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); 304 if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { 305 /* length(tag + length + unused bit count + bitstring) */ 306 return rc + (int) len; 307 } 308 309 return -1; 310} 311 312int 313ber_put_null( BerElement *ber, ber_tag_t tag ) 314{ 315 unsigned char data[TAGBUF_SIZE + 1], *ptr; 316 317 if ( tag == LBER_DEFAULT ) { 318 tag = LBER_NULL; 319 } 320 321 data[sizeof(data) - 1] = 0; /* length */ 322 ptr = ber_prepend_tag( &data[sizeof(data) - 1], tag ); 323 324 return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); 325} 326 327int 328ber_put_boolean( 329 BerElement *ber, 330 ber_int_t boolval, 331 ber_tag_t tag ) 332{ 333 unsigned char data[TAGBUF_SIZE + 2], *ptr; 334 335 if ( tag == LBER_DEFAULT ) 336 tag = LBER_BOOLEAN; 337 338 data[sizeof(data) - 1] = boolval ? 0xff : 0; 339 data[sizeof(data) - 2] = 1; /* length */ 340 ptr = ber_prepend_tag( &data[sizeof(data) - 2], tag ); 341 342 return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); 343} 344 345 346/* Max number of length octets in a sequence or set, normally 5 */ 347#define SOS_LENLEN (1 + (sizeof(ber_elem_size_t) > MAXINT_BERSIZE_OCTETS ? \ 348 (ber_len_t) sizeof(ber_elem_size_t) : MAXINT_BERSIZE_OCTETS)) 349 350/* Header of incomplete sequence or set */ 351typedef struct seqorset_header { 352 char xtagbuf[TAGBUF_SIZE + 1]; /* room for tag + len(tag or len) */ 353 union { 354 ber_elem_size_t offset; /* enclosing seqence/set */ 355 char padding[SOS_LENLEN-1]; /* for final length encoding */ 356 } next_sos; 357# define SOS_TAG_END(header) ((unsigned char *) &(header).next_sos - 1) 358} Seqorset_header; 359 360/* Start a sequence or set */ 361static int 362ber_start_seqorset( 363 BerElement *ber, 364 ber_tag_t tag ) 365{ 366 /* 367 * Write the tag and SOS_LENLEN octets reserved for length, to ber. 368 * For now, length octets = (tag length, previous ber_sos_inner). 369 * 370 * Update ber_sos_inner and the write-cursor ber_sos_ptr. ber_ptr 371 * will not move until the outermost sequence or set is complete. 372 */ 373 374 Seqorset_header header; 375 unsigned char *headptr; 376 ber_len_t taglen, headlen; 377 char *dest, **p; 378 379 assert( ber != NULL ); 380 assert( LBER_VALID( ber ) ); 381 382 if ( ber->ber_sos_ptr == NULL ) { /* outermost sequence/set? */ 383 header.next_sos.offset = 0; 384 p = &ber->ber_ptr; 385 } else { 386 if ( (ber_len_t) -1 > (ber_elem_size_t) -1 ) { 387 if ( ber->ber_sos_inner > (ber_elem_size_t) -1 ) 388 return -1; 389 } 390 header.next_sos.offset = ber->ber_sos_inner; 391 p = &ber->ber_sos_ptr; 392 } 393 headptr = ber_prepend_tag( SOS_TAG_END(header), tag ); 394 *SOS_TAG_END(header) = taglen = SOS_TAG_END(header) - headptr; 395 headlen = taglen + SOS_LENLEN; 396 397 /* As ber_write(,headptr,headlen,) except update ber_sos_ptr, not *p */ 398 if ( headlen > (ber_len_t) (ber->ber_end - *p) ) { 399 if ( ber_realloc( ber, headlen ) != 0 ) 400 return -1; 401 } 402 dest = *p; 403 AC_MEMCPY( dest, headptr, headlen ); 404 ber->ber_sos_ptr = dest + headlen; 405 406 ber->ber_sos_inner = dest + taglen - ber->ber_buf; 407 408 /* 409 * Do not return taglen + SOS_LENLEN here - then ber_put_seqorset() 410 * should return lenlen - SOS_LENLEN + len, which can be < 0. 411 */ 412 return 0; 413} 414 415int 416ber_start_seq( BerElement *ber, ber_tag_t tag ) 417{ 418 if ( tag == LBER_DEFAULT ) { 419 tag = LBER_SEQUENCE; 420 } 421 422 return ber_start_seqorset( ber, tag ); 423} 424 425int 426ber_start_set( BerElement *ber, ber_tag_t tag ) 427{ 428 if ( tag == LBER_DEFAULT ) { 429 tag = LBER_SET; 430 } 431 432 return ber_start_seqorset( ber, tag ); 433} 434 435/* End a sequence or set */ 436static int 437ber_put_seqorset( BerElement *ber ) 438{ 439 Seqorset_header header; 440 unsigned char *lenptr; /* length octets in the sequence/set */ 441 ber_len_t len; /* length(contents) */ 442 ber_len_t xlen; /* len + length(length) */ 443 444 assert( ber != NULL ); 445 assert( LBER_VALID( ber ) ); 446 447 if ( ber->ber_sos_ptr == NULL ) return -1; 448 449 lenptr = (unsigned char *) ber->ber_buf + ber->ber_sos_inner; 450 xlen = ber->ber_sos_ptr - (char *) lenptr; 451 if ( xlen > MAXINT_BERSIZE + SOS_LENLEN ) { 452 return -1; 453 } 454 455 /* Extract sequence/set information from length octets */ 456 memcpy( SOS_TAG_END(header), lenptr, SOS_LENLEN ); 457 458 /* Store length, and close gap of leftover reserved length octets */ 459 len = xlen - SOS_LENLEN; 460 if ( !(ber->ber_options & LBER_USE_DER) ) { 461 int i; 462 lenptr[0] = SOS_LENLEN - 1 + 0x80; /* length(length)-1 */ 463 for( i = SOS_LENLEN; --i > 0; len >>= 8 ) { 464 lenptr[i] = len & 0xffU; 465 } 466 } else { 467 unsigned char *p = ber_prepend_len( lenptr + SOS_LENLEN, len ); 468 ber_len_t unused = p - lenptr; 469 if ( unused != 0 ) { 470 /* length(length) < the reserved SOS_LENLEN bytes */ 471 xlen -= unused; 472 AC_MEMCPY( lenptr, p, xlen ); 473 ber->ber_sos_ptr = (char *) lenptr + xlen; 474 } 475 } 476 477 ber->ber_sos_inner = header.next_sos.offset; 478 if ( header.next_sos.offset == 0 ) { /* outermost sequence/set? */ 479 /* The ber_ptr is at the set/seq start - move it to the end */ 480 ber->ber_ptr = ber->ber_sos_ptr; 481 ber->ber_sos_ptr = NULL; 482 } 483 484 return xlen + *SOS_TAG_END(header); /* lenlen + len + taglen */ 485} 486 487int 488ber_put_seq( BerElement *ber ) 489{ 490 return ber_put_seqorset( ber ); 491} 492 493int 494ber_put_set( BerElement *ber ) 495{ 496 return ber_put_seqorset( ber ); 497} 498 499/* N tag */ 500static ber_tag_t lber_int_null = 0; 501 502/* VARARGS */ 503int 504ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... ) 505{ 506 va_list ap; 507 char *s, **ss; 508 struct berval *bv, **bvp; 509 int rc; 510 ber_int_t i; 511 ber_len_t len; 512 513 assert( ber != NULL ); 514 assert( fmt != NULL ); 515 assert( LBER_VALID( ber ) ); 516 517 va_start( ap, fmt ); 518 519 for ( rc = 0; *fmt && rc != -1; fmt++ ) { 520 switch ( *fmt ) { 521 case '!': { /* hook */ 522 BEREncodeCallback *f; 523 void *p; 524 525 ber->ber_usertag = 0; 526 527 f = va_arg( ap, BEREncodeCallback * ); 528 p = va_arg( ap, void * ); 529 rc = (*f)( ber, p ); 530 531 if ( ber->ber_usertag ) { 532 goto next; 533 } 534 } break; 535 536 case 'b': /* boolean */ 537 i = va_arg( ap, ber_int_t ); 538 rc = ber_put_boolean( ber, i, ber->ber_tag ); 539 break; 540 541 case 'i': /* int */ 542 i = va_arg( ap, ber_int_t ); 543 rc = ber_put_int( ber, i, ber->ber_tag ); 544 break; 545 546 case 'e': /* enumeration */ 547 i = va_arg( ap, ber_int_t ); 548 rc = ber_put_enum( ber, i, ber->ber_tag ); 549 break; 550 551 case 'n': /* null */ 552 rc = ber_put_null( ber, ber->ber_tag ); 553 break; 554 555 case 'N': /* Debug NULL */ 556 rc = 0; 557 if( lber_int_null != 0 ) { 558 /* Insert NULL to ensure peer ignores unknown tags */ 559 rc = ber_put_null( ber, lber_int_null ); 560 } 561 break; 562 563 case 'o': /* octet string (non-null terminated) */ 564 s = va_arg( ap, char * ); 565 len = va_arg( ap, ber_len_t ); 566 rc = ber_put_ostring( ber, s, len, ber->ber_tag ); 567 break; 568 569 case 'O': /* berval octet string */ 570 bv = va_arg( ap, struct berval * ); 571 if( bv == NULL ) break; 572 rc = ber_put_berval( ber, bv, ber->ber_tag ); 573 break; 574 575 case 's': /* string */ 576 s = va_arg( ap, char * ); 577 rc = ber_put_string( ber, s, ber->ber_tag ); 578 break; 579 580 case 'B': /* bit string */ 581 case 'X': /* bit string (deprecated) */ 582 s = va_arg( ap, char * ); 583 len = va_arg( ap, ber_len_t ); /* in bits */ 584 rc = ber_put_bitstring( ber, s, len, ber->ber_tag ); 585 break; 586 587 case 't': /* tag for the next element */ 588 ber->ber_tag = va_arg( ap, ber_tag_t ); 589 goto next; 590 591 case 'v': /* vector of strings */ 592 if ( (ss = va_arg( ap, char ** )) == NULL ) 593 break; 594 for ( i = 0; ss[i] != NULL; i++ ) { 595 if ( (rc = ber_put_string( ber, ss[i], 596 ber->ber_tag )) == -1 ) 597 break; 598 } 599 break; 600 601 case 'V': /* sequences of strings + lengths */ 602 if ( (bvp = va_arg( ap, struct berval ** )) == NULL ) 603 break; 604 for ( i = 0; bvp[i] != NULL; i++ ) { 605 if ( (rc = ber_put_berval( ber, bvp[i], 606 ber->ber_tag )) == -1 ) 607 break; 608 } 609 break; 610 611 case 'W': /* BerVarray */ 612 if ( (bv = va_arg( ap, BerVarray )) == NULL ) 613 break; 614 for ( i = 0; bv[i].bv_val != NULL; i++ ) { 615 if ( (rc = ber_put_berval( ber, &bv[i], 616 ber->ber_tag )) == -1 ) 617 break; 618 } 619 break; 620 621 case '{': /* begin sequence */ 622 rc = ber_start_seq( ber, ber->ber_tag ); 623 break; 624 625 case '}': /* end sequence */ 626 rc = ber_put_seqorset( ber ); 627 break; 628 629 case '[': /* begin set */ 630 rc = ber_start_set( ber, ber->ber_tag ); 631 break; 632 633 case ']': /* end set */ 634 rc = ber_put_seqorset( ber ); 635 break; 636 637 default: 638 if( ber->ber_debug ) { 639 ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, 640 "ber_printf: unknown fmt %c\n", *fmt ); 641 } 642 rc = -1; 643 break; 644 } 645 646 ber->ber_tag = LBER_DEFAULT; 647 next:; 648 } 649 650 va_end( ap ); 651 652 return rc; 653} 654