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