1 /* 2 * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 3 * 4 * The contents of this file are subject to the Netscape Public License 5 * Version 1.0 (the "NPL"); you may not use this file except in 6 * compliance with the NPL. You may obtain a copy of the NPL at 7 * http://www.mozilla.org/NPL/ 8 * 9 * Software distributed under the NPL is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL 11 * for the specific language governing rights and limitations under the 12 * NPL. 13 * 14 * The Initial Developer of this code under the NPL is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1998 Netscape Communications Corporation. All Rights 17 * Reserved. 18 */ 19 20 /* 21 * Copyright (c) 1990 Regents of the University of Michigan. 22 * All rights reserved. 23 * 24 * Redistribution and use in source and binary forms are permitted 25 * provided that this notice is preserved and that due credit is given 26 * to the University of Michigan at Ann Arbor. The name of the University 27 * may not be used to endorse or promote products derived from this 28 * software without specific prior written permission. This software 29 * is provided ``as is'' without express or implied warranty. 30 */ 31 32 /* 33 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 34 * Use is subject to license terms. 35 */ 36 37 #pragma ident "%Z%%M% %I% %E% SMI" 38 39 #include <sys/types.h> 40 #include <netinet/in.h> 41 #include <inttypes.h> 42 43 #include <ber_der.h> 44 #include "kmfber_int.h" 45 46 /* the following constants are used in kmfber_calc_lenlen */ 47 48 #define LENMASK1 0xFF 49 #define LENMASK2 0xFFFF 50 #define LENMASK3 0xFFFFFF 51 #define LENMASK4 0xFFFFFFFF 52 #define _MASK 0x80 53 54 int 55 kmfber_calc_taglen(ber_tag_t tag) 56 { 57 int i; 58 ber_int_t mask; 59 60 /* find the first non-all-zero byte in the tag */ 61 for (i = sizeof (ber_int_t) - 1; i > 0; i--) { 62 mask = (LENMASK3 << (i * 8)); 63 /* not all zero */ 64 if (tag & mask) 65 break; 66 } 67 68 return (i + 1); 69 } 70 71 static int 72 ber_put_tag(BerElement *ber, ber_tag_t tag, int nosos) 73 { 74 ber_int_t taglen; 75 ber_tag_t ntag; 76 77 taglen = kmfber_calc_taglen(tag); 78 79 ntag = htonl(tag); 80 81 return (kmfber_write(ber, 82 ((char *) &ntag) + sizeof (ber_int_t) - taglen, 83 taglen, nosos)); 84 } 85 86 int 87 kmfber_calc_lenlen(ber_int_t len) 88 { 89 /* 90 * short len if it's less than 128 - one byte giving the len, 91 * with bit 8 0. 92 */ 93 94 if (len <= 0x7F) 95 return (1); 96 97 /* 98 * long len otherwise - one byte with bit 8 set, giving the 99 * length of the length, followed by the length itself. 100 */ 101 102 if (len <= LENMASK1) 103 return (2); 104 if (len <= LENMASK2) 105 return (3); 106 if (len <= LENMASK3) 107 return (4); 108 109 return (5); 110 } 111 112 int 113 kmfber_put_len(BerElement *ber, ber_int_t len, int nosos) 114 { 115 int i; 116 char lenlen; 117 ber_int_t mask, netlen; 118 119 /* 120 * short len if it's less than 128 - one byte giving the len, 121 * with bit 8 0. 122 */ 123 if (len <= 127) { 124 netlen = htonl(len); 125 return (kmfber_write(ber, 126 (char *)&netlen + sizeof (ber_int_t) - 1, 127 1, nosos)); 128 } 129 130 /* 131 * long len otherwise - one byte with bit 8 set, giving the 132 * length of the length, followed by the length itself. 133 */ 134 135 /* find the first non-all-zero byte */ 136 for (i = sizeof (ber_int_t) - 1; i > 0; i--) { 137 mask = (LENMASK1 << (i * 8)); 138 /* not all zero */ 139 if (len & mask) 140 break; 141 } 142 lenlen = ++i; 143 if (lenlen > 4) 144 return (-1); 145 lenlen |= 0x80; 146 147 /* write the length of the length */ 148 if (kmfber_write(ber, &lenlen, 1, nosos) != 1) 149 return (-1); 150 151 /* write the length itself */ 152 netlen = htonl(len); 153 if (kmfber_write(ber, 154 (char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i) 155 return (-1); 156 157 return (i + 1); 158 } 159 160 static int 161 ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag) 162 { 163 int i, sign; 164 ber_int_t len, lenlen, taglen, netnum, mask; 165 166 sign = (num < 0); 167 168 /* 169 * high bit is set - look for first non-all-one byte 170 * high bit is clear - look for first non-all-zero byte 171 */ 172 for (i = sizeof (ber_int_t) - 1; i > 0; i--) { 173 mask = (LENMASK1 << (i * 8)); 174 175 if (sign) { 176 /* not all ones */ 177 if ((num & mask) != mask) 178 break; 179 } else { 180 /* not all zero */ 181 if (num & mask) 182 break; 183 } 184 } 185 186 /* 187 * we now have the "leading byte". if the high bit on this 188 * byte matches the sign bit, we need to "back up" a byte. 189 */ 190 mask = (num & (_MASK << (i * 8))); 191 if ((mask && !sign) || (sign && !mask)) 192 i++; 193 194 len = i + 1; 195 196 if ((taglen = ber_put_tag(ber, tag, 0)) == -1) 197 return (-1); 198 199 if ((lenlen = kmfber_put_len(ber, len, 0)) == -1) 200 return (-1); 201 i++; 202 netnum = htonl(num); 203 if (kmfber_write(ber, 204 (char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i) 205 /* length of tag + length + contents */ 206 return (taglen + lenlen + i); 207 208 return (-1); 209 } 210 211 static int 212 kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag) 213 { 214 if (tag == KMFBER_DEFAULT) 215 tag = BER_ENUMERATED; 216 217 return (ber_put_int_or_enum(ber, num, tag)); 218 } 219 220 int 221 ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag) 222 { 223 if (tag == KMFBER_DEFAULT) 224 tag = BER_INTEGER; 225 226 return (ber_put_int_or_enum(ber, num, tag)); 227 } 228 229 int 230 ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag) 231 { 232 ber_int_t taglen, lenlen, rc, len; 233 234 if (tag == KMFBER_DEFAULT) 235 tag = 0x06; /* TODO: Add new OID constant to header */ 236 237 if ((taglen = ber_put_tag(ber, tag, 0)) == -1) 238 return (-1); 239 240 len = (ber_int_t)oid->bv_len; 241 if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 || 242 kmfber_write(ber, oid->bv_val, oid->bv_len, 0) != 243 (ber_int_t)oid->bv_len) { 244 rc = -1; 245 } else { 246 /* return length of tag + length + contents */ 247 rc = taglen + lenlen + oid->bv_len; 248 } 249 return (rc); 250 } 251 252 int 253 ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data, 254 ber_len_t len) 255 { 256 ber_int_t taglen, lenlen, ilen, rc; 257 258 if (tag == KMFBER_DEFAULT) 259 tag = BER_INTEGER; 260 261 if ((taglen = ber_put_tag(ber, tag, 0)) == -1) 262 return (-1); 263 264 ilen = (ber_int_t)len; 265 if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 || 266 kmfber_write(ber, data, len, 0) != (ber_int_t)len) { 267 rc = -1; 268 } else { 269 /* return length of tag + length + contents */ 270 rc = taglen + lenlen + len; 271 } 272 return (rc); 273 } 274 275 static int 276 kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len, 277 ber_tag_t tag) 278 { 279 ber_int_t taglen, lenlen, ilen, rc; 280 #ifdef STR_TRANSLATION 281 int free_str; 282 #endif /* STR_TRANSLATION */ 283 284 if (tag == KMFBER_DEFAULT) 285 tag = BER_OCTET_STRING; 286 287 if ((taglen = ber_put_tag(ber, tag, 0)) == -1) 288 return (-1); 289 290 #ifdef STR_TRANSLATION 291 if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 && 292 ber->ber_encode_translate_proc != NULL) { 293 if ((*(ber->ber_encode_translate_proc))(&str, &len, 0) 294 != 0) { 295 return (-1); 296 } 297 free_str = 1; 298 } else { 299 free_str = 0; 300 } 301 #endif /* STR_TRANSLATION */ 302 303 /* 304 * Note: below is a spot where we limit ber_write 305 * to signed long (instead of unsigned long) 306 */ 307 ilen = (ber_int_t)len; 308 if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 || 309 kmfber_write(ber, str, len, 0) != (ber_int_t)len) { 310 rc = -1; 311 } else { 312 /* return length of tag + length + contents */ 313 rc = taglen + lenlen + len; 314 } 315 316 #ifdef STR_TRANSLATION 317 if (free_str) { 318 free(str); 319 } 320 #endif /* STR_TRANSLATION */ 321 322 return (rc); 323 } 324 325 static int 326 kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag) 327 { 328 return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag)); 329 } 330 331 static int 332 kmfber_put_bitstring(BerElement *ber, char *str, 333 ber_len_t blen /* in bits */, ber_tag_t tag) 334 { 335 ber_int_t taglen, lenlen, len; 336 unsigned char unusedbits; 337 338 if (tag == KMFBER_DEFAULT) 339 tag = BER_BIT_STRING; 340 341 if ((taglen = ber_put_tag(ber, tag, 0)) == -1) 342 return (-1); 343 344 len = (blen + 7) / 8; 345 unusedbits = (unsigned char) (len * 8 - blen); 346 if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1) 347 return (-1); 348 349 if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1) 350 return (-1); 351 352 if (kmfber_write(ber, str, len, 0) != len) 353 return (-1); 354 355 /* return length of tag + length + unused bit count + contents */ 356 return (taglen + 1 + lenlen + len); 357 } 358 359 static int 360 kmfber_put_null(BerElement *ber, ber_tag_t tag) 361 { 362 int taglen; 363 364 if (tag == KMFBER_DEFAULT) 365 tag = BER_NULL; 366 367 if ((taglen = ber_put_tag(ber, tag, 0)) == -1) 368 return (-1); 369 370 if (kmfber_put_len(ber, 0, 0) != 1) 371 return (-1); 372 373 return (taglen + 1); 374 } 375 376 static int 377 kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag) 378 { 379 int taglen; 380 unsigned char trueval = 0xff; 381 unsigned char falseval = 0x00; 382 383 if (tag == KMFBER_DEFAULT) 384 tag = BER_BOOLEAN; 385 386 if ((taglen = ber_put_tag(ber, tag, 0)) == -1) 387 return (-1); 388 389 if (kmfber_put_len(ber, 1, 0) != 1) 390 return (-1); 391 392 if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0) 393 != 1) 394 return (-1); 395 396 return (taglen + 2); 397 } 398 399 #define FOUR_BYTE_LEN 5 400 401 402 /* 403 * The idea here is roughly this: we maintain a stack of these Seqorset 404 * structures. This is pushed when we see the beginning of a new set or 405 * sequence. It is popped when we see the end of a set or sequence. 406 * Since we don't want to malloc and free these structures all the time, 407 * we pre-allocate a small set of them within the ber element structure. 408 * thus we need to spot when we've overflowed this stack and fall back to 409 * malloc'ing instead. 410 */ 411 static int 412 ber_start_seqorset(BerElement *ber, ber_tag_t tag) 413 { 414 Seqorset *new_sos; 415 416 /* can we fit into the local stack ? */ 417 if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) { 418 /* yes */ 419 new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn]; 420 } else { 421 /* no */ 422 if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset))) 423 == NULLSEQORSET) { 424 return (-1); 425 } 426 } 427 ber->ber_sos_stack_posn++; 428 429 if (ber->ber_sos == NULLSEQORSET) 430 new_sos->sos_first = ber->ber_ptr; 431 else 432 new_sos->sos_first = ber->ber_sos->sos_ptr; 433 434 /* Set aside room for a 4 byte length field */ 435 new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) + 436 FOUR_BYTE_LEN; 437 new_sos->sos_tag = tag; 438 439 new_sos->sos_next = ber->ber_sos; 440 new_sos->sos_clen = 0; 441 442 ber->ber_sos = new_sos; 443 if (ber->ber_sos->sos_ptr > ber->ber_end) { 444 (void) realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end); 445 } 446 return (0); 447 } 448 449 static int 450 kmfber_start_seq(BerElement *ber, ber_tag_t tag) 451 { 452 if (tag == KMFBER_DEFAULT) 453 tag = BER_CONSTRUCTED_SEQUENCE; 454 455 return (ber_start_seqorset(ber, tag)); 456 } 457 458 static int 459 kmfber_start_set(BerElement *ber, ber_tag_t tag) 460 { 461 if (tag == KMFBER_DEFAULT) 462 tag = BER_CONSTRUCTED_SET; 463 464 return (ber_start_seqorset(ber, tag)); 465 } 466 467 static int 468 ber_put_seqorset(BerElement *ber) 469 { 470 ber_int_t netlen, len, taglen, lenlen; 471 unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1; 472 Seqorset *next; 473 Seqorset **sos = &ber->ber_sos; 474 475 /* 476 * If this is the toplevel sequence or set, we need to actually 477 * write the stuff out. Otherwise, it's already been put in 478 * the appropriate buffer and will be written when the toplevel 479 * one is written. In this case all we need to do is update the 480 * length and tag. 481 */ 482 483 len = (*sos)->sos_clen; 484 netlen = (ber_len_t)htonl(len); 485 486 if (ber->ber_options & KMFBER_OPT_USE_DER) { 487 lenlen = kmfber_calc_lenlen(len); 488 } else { 489 lenlen = FOUR_BYTE_LEN; 490 } 491 492 if ((next = (*sos)->sos_next) == NULLSEQORSET) { 493 /* write the tag */ 494 if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1) 495 return (-1); 496 497 if (ber->ber_options & KMFBER_OPT_USE_DER) { 498 /* Write the length in the minimum # of octets */ 499 if (kmfber_put_len(ber, len, 1) == -1) 500 return (-1); 501 502 if (lenlen != FOUR_BYTE_LEN) { 503 /* 504 * We set aside FOUR_BYTE_LEN bytes for 505 * the length field. Move the data if 506 * we don't actually need that much 507 */ 508 (void) memmove((*sos)->sos_first + taglen + 509 lenlen, (*sos)->sos_first + taglen + 510 FOUR_BYTE_LEN, len); 511 } 512 } else { 513 /* Fill FOUR_BYTE_LEN bytes for length field */ 514 /* one byte of length length */ 515 if (kmfber_write(ber, (char *)<ag, 1, 1) != 1) 516 return (-1); 517 518 /* the length itself */ 519 if (kmfber_write(ber, 520 (char *)&netlen + sizeof (ber_int_t) 521 - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) != 522 FOUR_BYTE_LEN - 1) 523 return (-1); 524 } 525 /* The ber_ptr is at the set/seq start - move it to the end */ 526 ber->ber_ptr += len; 527 } else { 528 ber_tag_t ntag; 529 530 /* the tag */ 531 taglen = kmfber_calc_taglen((*sos)->sos_tag); 532 ntag = htonl((*sos)->sos_tag); 533 (void) memmove((*sos)->sos_first, (char *)&ntag + 534 sizeof (ber_int_t) - taglen, taglen); 535 536 if (ber->ber_options & KMFBER_OPT_USE_DER) { 537 ltag = (lenlen == 1) ? (unsigned char)len : 538 (unsigned char) (0x80 + (lenlen - 1)); 539 } 540 541 /* one byte of length length */ 542 (void) memmove((*sos)->sos_first + 1, <ag, 1); 543 544 if (ber->ber_options & KMFBER_OPT_USE_DER) { 545 if (lenlen > 1) { 546 /* Write the length itself */ 547 (void) memmove((*sos)->sos_first + 2, 548 (char *)&netlen + sizeof (ber_uint_t) - 549 (lenlen - 1), 550 lenlen - 1); 551 } 552 if (lenlen != FOUR_BYTE_LEN) { 553 /* 554 * We set aside FOUR_BYTE_LEN bytes for 555 * the length field. Move the data if 556 * we don't actually need that much 557 */ 558 (void) memmove((*sos)->sos_first + taglen + 559 lenlen, (*sos)->sos_first + taglen + 560 FOUR_BYTE_LEN, len); 561 } 562 } else { 563 /* the length itself */ 564 (void) memmove((*sos)->sos_first + taglen + 1, 565 (char *) &netlen + sizeof (ber_int_t) - 566 (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1); 567 } 568 569 next->sos_clen += (taglen + lenlen + len); 570 next->sos_ptr += (taglen + lenlen + len); 571 } 572 573 /* we're done with this seqorset, so free it up */ 574 /* was this one from the local stack ? */ 575 if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) { 576 free((char *)(*sos)); 577 } 578 ber->ber_sos_stack_posn--; 579 *sos = next; 580 581 return (taglen + lenlen + len); 582 } 583 584 /* VARARGS */ 585 int 586 kmfber_printf(BerElement *ber, const char *fmt, ...) 587 { 588 va_list ap; 589 char *s, **ss; 590 struct berval **bv, *oid; 591 int rc, i, t; 592 ber_int_t len; 593 594 va_start(ap, fmt); 595 596 #ifdef KMFBER_DEBUG 597 if (lber_debug & 64) { 598 char msg[80]; 599 sprintf(msg, "kmfber_printf fmt (%s)\n", fmt); 600 ber_err_print(msg); 601 } 602 #endif 603 604 for (rc = 0; *fmt && rc != -1; fmt++) { 605 switch (*fmt) { 606 case 'b': /* boolean */ 607 i = va_arg(ap, int); 608 rc = kmfber_put_boolean(ber, i, ber->ber_tag); 609 break; 610 611 case 'i': /* int */ 612 i = va_arg(ap, int); 613 rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag); 614 break; 615 616 case 'D': /* Object ID */ 617 if ((oid = va_arg(ap, struct berval *)) == NULL) 618 break; 619 rc = ber_put_oid(ber, oid, ber->ber_tag); 620 break; 621 case 'I': /* int */ 622 s = va_arg(ap, char *); 623 len = va_arg(ap, ber_int_t); 624 rc = ber_put_big_int(ber, ber->ber_tag, s, len); 625 break; 626 627 case 'e': /* enumeration */ 628 i = va_arg(ap, int); 629 rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag); 630 break; 631 632 case 'l': 633 t = va_arg(ap, int); 634 rc = kmfber_put_len(ber, t, 0); 635 break; 636 case 'n': /* null */ 637 rc = kmfber_put_null(ber, ber->ber_tag); 638 break; 639 640 case 'o': /* octet string (non-null terminated) */ 641 s = va_arg(ap, char *); 642 len = va_arg(ap, int); 643 rc = kmfber_put_ostring(ber, s, len, ber->ber_tag); 644 break; 645 646 case 's': /* string */ 647 s = va_arg(ap, char *); 648 rc = kmfber_put_string(ber, s, ber->ber_tag); 649 break; 650 651 case 'B': /* bit string */ 652 s = va_arg(ap, char *); 653 len = va_arg(ap, int); /* in bits */ 654 rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag); 655 break; 656 657 case 't': /* tag for the next element */ 658 ber->ber_tag = va_arg(ap, ber_tag_t); 659 ber->ber_usertag = 1; 660 break; 661 662 case 'T': /* Write an explicit tag, but don't change current */ 663 t = va_arg(ap, int); 664 rc = ber_put_tag(ber, t, 0); 665 break; 666 667 case 'v': /* vector of strings */ 668 if ((ss = va_arg(ap, char **)) == NULL) 669 break; 670 for (i = 0; ss[i] != NULL; i++) { 671 if ((rc = kmfber_put_string(ber, ss[i], 672 ber->ber_tag)) == -1) 673 break; 674 } 675 break; 676 677 case 'V': /* sequences of strings + lengths */ 678 if ((bv = va_arg(ap, struct berval **)) == NULL) 679 break; 680 for (i = 0; bv[i] != NULL; i++) { 681 if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val, 682 bv[i]->bv_len, ber->ber_tag)) == -1) 683 break; 684 } 685 break; 686 687 case '{': /* begin sequence */ 688 rc = kmfber_start_seq(ber, ber->ber_tag); 689 break; 690 691 case '}': /* end sequence */ 692 rc = ber_put_seqorset(ber); 693 break; 694 695 case '[': /* begin set */ 696 rc = kmfber_start_set(ber, ber->ber_tag); 697 break; 698 699 case ']': /* end set */ 700 rc = ber_put_seqorset(ber); 701 break; 702 703 default: { 704 #ifdef KMFBER_DEBUG 705 char msg[80]; 706 sprintf(msg, "unknown fmt %c\n", *fmt); 707 ber_err_print(msg); 708 #endif 709 rc = -1; 710 break; 711 } 712 } 713 714 if (ber->ber_usertag == 0) 715 ber->ber_tag = KMFBER_DEFAULT; 716 else 717 ber->ber_usertag = 0; 718 } 719 720 va_end(ap); 721 722 return (rc); 723 } 724