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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * ASN.1 encoding related routines 31 */ 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/types.h> 37 #include "asn1.h" 38 #include "pdu.h" 39 #include "debug.h" 40 41 /* 42 * This routine builds a 'SEQUENCE OF' ASN.1 object in the buffer 43 * using the 'id' and 'length' supplied. This is probably the place 44 * where using "reverse" asn encoding will help. 45 */ 46 uchar_t * 47 asn_build_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length) 48 { 49 /* 50 * When rebuilding sequence (which we do many times), we'll 51 * simply pass NULL to bufsz_p to skip the error check. 52 */ 53 if ((bufsz_p) && (*bufsz_p < 4)) 54 return (NULL); 55 56 buf[0] = id; 57 buf[1] = (uchar_t)(ASN_LONG_LEN | 0x02); /* following 2 octets */ 58 buf[2] = (uchar_t)((length >> 8) & 0xff); 59 buf[3] = (uchar_t)(length & 0xff); 60 61 if (bufsz_p) 62 *bufsz_p -= 4; 63 64 LOGASNSEQ(buf, 4); 65 66 return (buf + 4); 67 } 68 69 /* 70 * The next two routines, asn_build_header() and asn_build_length(), build 71 * the header and length for an arbitrary object type into the buffer. The 72 * length of the object is encoded using as few length octets as possible. 73 */ 74 uchar_t * 75 asn_build_header(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length) 76 { 77 if (*bufsz_p < 1) 78 return (NULL); 79 80 buf[0] = id; 81 (*bufsz_p)--; 82 83 return (asn_build_length(buf + 1, bufsz_p, length)); 84 } 85 uchar_t * 86 asn_build_length(uchar_t *buf, size_t *bufsz_p, size_t length) 87 { 88 if (length < 0x80) { 89 if (*bufsz_p < 1) 90 return (NULL); 91 buf[0] = (uchar_t)length; 92 (*bufsz_p)--; 93 94 LOGASNLENGTH(buf, 1); 95 96 return (buf + 1); 97 98 } else if (length <= 0xFF) { 99 if (*bufsz_p < 2) 100 return (NULL); 101 buf[0] = (uchar_t)(ASN_LONG_LEN | 0x01); 102 buf[1] = (uchar_t)length; 103 *bufsz_p -= 2; 104 105 LOGASNLENGTH(buf, 2); 106 107 return (buf + 2); 108 109 } else { 110 if (*bufsz_p < 3) 111 return (NULL); 112 113 buf[0] = (uchar_t)(ASN_LONG_LEN | 0x02); 114 buf[1] = (uchar_t)((length >> 8) & 0xff); 115 buf[2] = (uchar_t)(length & 0xff); 116 *bufsz_p -= 3; 117 118 LOGASNLENGTH(buf, 3); 119 120 return (buf + 3); 121 } 122 } 123 /* 124 * Builds an ASN.1 encoded integer in the buffer using as few octets 125 * as possible. 126 */ 127 uchar_t * 128 asn_build_int(uchar_t *buf, size_t *bufsz_p, uchar_t id, int val) 129 { 130 uint_t uival; 131 int ival, i; 132 short sval; 133 char cval; 134 135 size_t valsz; 136 uchar_t *p, *valp; 137 138 /* 139 * We need to "pack" the integer before sending it, so determine 140 * the minimum number of bytes in which we can pack the integer 141 */ 142 uival = ((uint_t)val >> BUILD_INT_SHIFT) & BUILD_INT_MASK; 143 ival = val; 144 sval = (short)val; /* yes, loss of data intended */ 145 cval = (char)val; /* yes, loss of data intended */ 146 147 if (val == (int)cval) 148 valsz = 1; 149 else if (val == (int)sval) 150 valsz = 2; 151 else if (uival == BUILD_INT_MASK || uival == 0) 152 valsz = 3; 153 else 154 valsz = 4; 155 156 /* 157 * Prepare the ASN.1 header for the integer 158 */ 159 if ((p = asn_build_header(buf, bufsz_p, id, valsz)) == NULL) 160 return (NULL); 161 162 /* 163 * If we have enough space left, encode the integer 164 */ 165 if (*bufsz_p < valsz) 166 return (NULL); 167 else { 168 valp = (uchar_t *)&ival; 169 for (i = 0; i < valsz; i++) 170 p[i] = valp[sizeof (int) - valsz + i]; 171 172 *bufsz_p -= valsz; 173 174 LOGASNINT(buf, p + valsz - buf); 175 176 return (p + valsz); 177 } 178 } 179 /* 180 * Builds an ASN.1 encoded octet string in the buffer. The source string 181 * need not be null-terminated. 182 */ 183 uchar_t * 184 asn_build_string(uchar_t *buf, size_t *bufsz_p, uchar_t id, uchar_t *str, 185 size_t slen) 186 { 187 uchar_t *p; 188 189 if ((p = asn_build_header(buf, bufsz_p, id, slen)) == NULL) 190 return (NULL); 191 192 if (*bufsz_p < slen) 193 return (NULL); 194 else { 195 if (str) { 196 (void) memcpy(p, str, slen); 197 } else { 198 (void) memset(p, 0, slen); 199 } 200 201 *bufsz_p -= slen; 202 203 LOGASNOCTSTR(buf, p + slen - buf); 204 205 return (p + slen); 206 } 207 } 208 209 /* 210 * Builds an Object Identifier into the buffer according to the OID 211 * packing and encoding rules. 212 */ 213 uchar_t * 214 asn_build_objid(uchar_t *buf, size_t *bufsz_p, uchar_t id, void *oidp, 215 size_t n_subids) 216 { 217 oid *objid = oidp; 218 size_t oid_asnlen; 219 oid subid, first_subid; 220 uchar_t subid_len[MAX_SUBIDS_IN_OID]; 221 uchar_t *p; 222 int i, ndx; 223 224 /* 225 * Eliminate invalid cases 226 */ 227 if (n_subids < MIN_SUBIDS_IN_OID || n_subids > MAX_SUBIDS_IN_OID) 228 return (NULL); 229 if ((objid[0] > 2) || (objid[0] < 2 && objid[1] >= 40)) 230 return (NULL); 231 232 /* 233 * The BER encoding rule for the ASN.1 Object Identifier states 234 * that after packing the first two subids into one, each subsequent 235 * component is considered as the next subid. Each subidentifier is 236 * then encoded as a non-negative integer using as few 7-bit blocks 237 * as possible. The blocks are packed in octets with the first bit of 238 * each octet equal to 1, except for the last octet of each subid. 239 */ 240 oid_asnlen = 0; 241 for (i = 0, ndx = 0; i < n_subids; i++, ndx++) { 242 if (i == 0) { 243 /* 244 * The packing formula for the first two subids 245 * of an OID is given by Z = (X * 40) + Y 246 */ 247 subid = objid[0] * 40 + objid[1]; 248 first_subid = subid; 249 i++; /* done with both subids 0 and 1 */ 250 } else { 251 subid = objid[i]; 252 } 253 254 if (subid < (oid) 0x80) 255 subid_len[ndx] = 1; 256 else if (subid < (oid) 0x4000) 257 subid_len[ndx] = 2; 258 else if (subid < (oid) 0x200000) 259 subid_len[ndx] = 3; 260 else if (subid < (oid) 0x10000000) 261 subid_len[ndx] = 4; 262 else { 263 subid_len[ndx] = 5; 264 } 265 266 oid_asnlen += subid_len[ndx]; 267 } 268 269 if ((p = asn_build_header(buf, bufsz_p, id, oid_asnlen)) == NULL) 270 return (NULL); 271 272 if (*bufsz_p < oid_asnlen) 273 return (NULL); 274 275 /* 276 * Store the encoded OID 277 */ 278 for (i = 0, ndx = 0; i < n_subids; i++, ndx++) { 279 if (i == 0) { 280 subid = first_subid; 281 i++; 282 } else { 283 subid = objid[i]; 284 } 285 286 switch (subid_len[ndx]) { 287 case 1: 288 *p++ = (uchar_t)subid; 289 break; 290 291 case 2: 292 *p++ = (uchar_t)((subid >> 7) | 0x80); 293 *p++ = (uchar_t)(subid & 0x7f); 294 break; 295 296 case 3: 297 *p++ = (uchar_t)((subid >> 14) | 0x80); 298 *p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80); 299 *p++ = (uchar_t)(subid & 0x7f); 300 break; 301 302 case 4: 303 *p++ = (uchar_t)((subid >> 21) | 0x80); 304 *p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80); 305 *p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80); 306 *p++ = (uchar_t)(subid & 0x7f); 307 break; 308 309 case 5: 310 *p++ = (uchar_t)((subid >> 28) | 0x80); 311 *p++ = (uchar_t)(((subid >> 21) & 0x7f) | 0x80); 312 *p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80); 313 *p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80); 314 *p++ = (uchar_t)(subid & 0x7f); 315 break; 316 } 317 } 318 319 *bufsz_p -= oid_asnlen; 320 321 LOGASNOID(buf, p - buf); 322 323 return (p); 324 } 325 /* 326 * Build an ASN_NULL object val into the request packet 327 */ 328 uchar_t * 329 asn_build_null(uchar_t *buf, size_t *bufsz_p, uchar_t id) 330 { 331 uchar_t *p; 332 333 p = asn_build_header(buf, bufsz_p, id, 0); 334 335 LOGASNNULL(buf, p - buf); 336 337 return (p); 338 } 339 340 341 342 /* 343 * This routine parses a 'SEQUENCE OF' object header from the input 344 * buffer stream. If the identifier tag (made up of class, constructed 345 * type and data type tag) does not match the expected identifier tag, 346 * returns failure. 347 */ 348 uchar_t * 349 asn_parse_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t exp_id) 350 { 351 uchar_t *p; 352 uchar_t id; 353 354 if ((p = asn_parse_header(buf, bufsz_p, &id)) == NULL) 355 return (NULL); 356 357 if (id != exp_id) 358 return (NULL); 359 360 return (p); 361 } 362 /* 363 * Return the type identifier of the ASN object via 'id' 364 */ 365 uchar_t * 366 asn_parse_header(uchar_t *buf, size_t *bufsz_p, uchar_t *id) 367 { 368 uchar_t *p; 369 size_t asnobj_len, hdrlen; 370 371 /* 372 * Objects with extension tag type are not supported 373 */ 374 if ((buf[0] & ASN_EXT_TAG) == ASN_EXT_TAG) 375 return (NULL); 376 377 /* 378 * Parse the length field of the ASN object in the header 379 */ 380 if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL) 381 return (NULL); 382 383 /* 384 * Check if the rest of the msg packet is big enough for the 385 * full length of the object 386 */ 387 hdrlen = p - buf; 388 if (*bufsz_p < (asnobj_len + hdrlen)) 389 return (NULL); 390 391 *id = buf[0]; 392 *bufsz_p -= hdrlen; 393 394 return (p); 395 } 396 /* 397 * This routine parses the length of the object as specified in its 398 * header. The 'Indefinite' form of representing length is not supported. 399 */ 400 uchar_t * 401 asn_parse_length(uchar_t *buf, size_t *asnobj_len_p) 402 { 403 uchar_t *p; 404 int n_length_octets; 405 406 /* 407 * First, check for the short-definite form. Length of 408 * the object is simply the least significant 7-bits of 409 * the first byte. 410 */ 411 if ((buf[0] & ASN_LONG_LEN) == 0) { 412 *asnobj_len_p = (size_t)buf[0]; 413 return (buf + 1); 414 } 415 416 /* 417 * Then, eliminate the indefinite form. The ASN_LONG_LEN 418 * bit of the first byte will be set and the least significant 419 * 7-bites of that byte will be zeros. 420 */ 421 if (buf[0] == (uchar_t)ASN_LONG_LEN) 422 return (NULL); 423 424 /* 425 * Then, eliminate the long-definite case when the number of 426 * follow-up octets is more than what the size var can hold. 427 */ 428 n_length_octets = buf[0] & ~ASN_LONG_LEN; 429 if (n_length_octets > sizeof (*asnobj_len_p)) 430 return (NULL); 431 432 /* 433 * Finally gather the length 434 */ 435 p = buf + 1; 436 *asnobj_len_p = 0; 437 while (n_length_octets--) { 438 *asnobj_len_p <<= 8; 439 *asnobj_len_p |= *p++; 440 } 441 442 return (p); 443 } 444 /* 445 * Parses an integer out of the input buffer 446 */ 447 uchar_t * 448 asn_parse_int(uchar_t *buf, size_t *bufsz_p, int *ival) 449 { 450 size_t asnobj_len, hdrlen; 451 uchar_t int_id; 452 uchar_t *p; 453 454 int_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER; 455 if (buf[0] != int_id) 456 return (NULL); 457 458 /* 459 * Read in the length of the object; Note that integers are 460 * "packed" when sent from agent to manager and vice-versa, 461 * so the size of the object could be less than sizeof (int). 462 */ 463 if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL) 464 return (NULL); 465 466 /* 467 * Is there sufficient space left in the packet to read the integer ? 468 */ 469 hdrlen = p - buf; 470 if (*bufsz_p < (hdrlen + asnobj_len)) 471 return (NULL); 472 473 /* 474 * Update space left in the buffer after the integer is read 475 */ 476 *bufsz_p -= (hdrlen + asnobj_len); 477 478 /* 479 * Read in the integer value 480 */ 481 *ival = (*p & ASN_BIT8) ? -1 : 0; 482 while (asnobj_len--) { 483 *ival <<= 8; 484 *ival |= *p++; 485 } 486 487 return (p); 488 } 489 /* 490 * Parses an unsigned integer out of the input buffer 491 */ 492 uchar_t * 493 asn_parse_uint(uchar_t *buf, size_t *bufsz_p, uint_t *uival) 494 { 495 size_t asnobj_len, hdrlen; 496 uchar_t *p; 497 498 if ((buf[0] != ASN_COUNTER) && (buf[0] != ASN_TIMETICKS)) 499 return (NULL); 500 501 /* 502 * Read in the length of the object. Integers are sent the same 503 * way unsigned integers are sent. Except that, if the MSB was 1 504 * in the unsigned int value, a null-byte is attached to the front. 505 * Otherwise, packing rules are the same as for integer values. 506 */ 507 if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL) 508 return (NULL); 509 510 /* 511 * Is there sufficient space left in the packet to read in the value ? 512 */ 513 hdrlen = p - buf; 514 if (*bufsz_p < (hdrlen + asnobj_len)) 515 return (NULL); 516 517 /* 518 * Update space left in the buffer after the uint is read 519 */ 520 *bufsz_p -= (hdrlen + asnobj_len); 521 522 /* 523 * Read in the unsigned integer (this should never get 524 * initialized to ~0 if it was sent right) 525 */ 526 *uival = (*p & ASN_BIT8) ? ~0 : 0; 527 while (asnobj_len--) { 528 *uival <<= 8; 529 *uival |= *p++; 530 } 531 532 return (p); 533 } 534 /* 535 * Parses a string (ASN_OCTET_STR or ASN_BIT_STR) out of the input buffer. 536 * The memory for the string is allocated inside the routine and must be 537 * freed by the caller when it is no longer needed. If the string type is 538 * ASN_OCTET_STR, the returned string is null-terminated, and the returned 539 * length indicates the strlen value. If the string type is ASN_BIT_STR, 540 * the returned string is not null-terminated, and the returned length 541 * indicates the number of bytes. 542 */ 543 uchar_t * 544 asn_parse_string(uchar_t *buf, size_t *bufsz_p, uchar_t **str_p, size_t *slen) 545 { 546 uchar_t *p; 547 uchar_t id1, id2; 548 size_t asnobj_len, hdrlen; 549 550 /* 551 * Octet and bit strings are supported 552 */ 553 id1 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR; 554 id2 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR; 555 if ((buf[0] != id1) && (buf[0] != id2)) 556 return (NULL); 557 558 /* 559 * Parse out the length of the object and verify source buf sz 560 */ 561 if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL) 562 return (NULL); 563 564 hdrlen = p - buf; 565 if (*bufsz_p < (hdrlen + asnobj_len)) 566 return (NULL); 567 568 /* 569 * Allocate for and copy out the string 570 */ 571 if ((*str_p = (uchar_t *)calloc(1, asnobj_len + 1)) == NULL) 572 return (NULL); 573 574 (void) memcpy(*str_p, p, asnobj_len); 575 576 /* 577 * Terminate the octet string with a null 578 */ 579 if (buf[0] == id1) { 580 (*str_p)[asnobj_len] = 0; 581 } 582 583 /* 584 * Update pointers and return 585 */ 586 *slen = asnobj_len; 587 *bufsz_p -= (hdrlen + asnobj_len); 588 589 return (p + asnobj_len); 590 } 591 /* 592 * Parses an object identifier out of the input packet buffer. Space for 593 * the oid object is allocated within this routine and must be freed by the 594 * caller when no longer needed. 595 */ 596 uchar_t * 597 asn_parse_objid(uchar_t *msg, size_t *varsz_p, void *oidp, size_t *n_subids) 598 { 599 oid **objid_p = oidp; 600 oid *objid; 601 uchar_t *p; 602 size_t hdrlen, asnobj_len; 603 oid subid; 604 int i, ndx; 605 uchar_t exp_id; 606 607 /* 608 * Check id 609 */ 610 exp_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID; 611 if (msg[0] != exp_id) 612 return (NULL); 613 614 /* 615 * Read object length 616 */ 617 if ((p = asn_parse_length(msg + 1, &asnobj_len)) == NULL) 618 return (NULL); 619 620 /* 621 * Check space in input message 622 */ 623 hdrlen = p - msg; 624 if (*varsz_p < (hdrlen + asnobj_len)) 625 return (NULL); 626 627 /* 628 * Since the OID subidentifiers are packed in 7-bit blocks with 629 * MSB set to 1 for all but the last octet, the number of subids 630 * is simply the number of octets with MSB equal to 0, plus 1 631 * (since the first two subids were packed into one subid and have 632 * to be expanded back to two). 633 */ 634 *n_subids = 1; 635 for (i = 0; i < asnobj_len; i++) { 636 if ((p[i] & ASN_BIT8) == 0) 637 (*n_subids)++; 638 } 639 640 /* 641 * Now allocate for the oid and parse the OID into it 642 */ 643 if ((objid = (oid *) calloc(1, (*n_subids) * sizeof (oid))) == NULL) 644 return (NULL); 645 646 ndx = 1; /* start from 1 to allow for unpacking later */ 647 subid = 0; 648 for (i = 0; i < asnobj_len; i++) { 649 subid = subid << 7; 650 subid |= (p[i] & ~ASN_BIT8); 651 652 if ((p[i] & ASN_BIT8) == 0) { 653 objid[ndx] = subid; 654 ndx++; 655 subid = 0; 656 } 657 } 658 659 /* 660 * Now unpack the first two subids from the subid at index 1. 661 */ 662 if (objid[1] < 40) { 663 objid[0] = 0; 664 } else if (objid[1] < 80) { 665 objid[0] = 1; 666 objid[1] -= 40; 667 } else { 668 objid[0] = 2; 669 objid[1] -= 80; 670 } 671 672 *objid_p = objid; 673 *varsz_p -= (hdrlen + asnobj_len); 674 675 return (msg + hdrlen + asnobj_len); 676 } 677 /* 678 * Parses the value of an OID object out of the input message buffer. 679 * Only type tags less than ASN_EXT_TAG (0x1f) are supported. 680 */ 681 uchar_t * 682 asn_parse_objval(uchar_t *msg, size_t *varsz_p, void *varlistp) 683 { 684 pdu_varlist_t *vp = varlistp; 685 uchar_t *p; 686 size_t n_subids; 687 size_t hdrlen, asnobj_len; 688 689 vp->type = msg[0] & ASN_EXT_TAG; 690 if (vp->type == ASN_EXT_TAG) 691 return (NULL); 692 693 /* 694 * Currently we handle ASN_INTEGER, ASN_OCTET_STR, ASN_BIT_STR 695 * and ASN_TIMETICKS types. 696 */ 697 switch (msg[0]) { 698 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER: 699 vp->val.iptr = (int *)calloc(1, sizeof (int)); 700 if (vp->val.iptr == NULL) 701 return (NULL); 702 703 if ((p = asn_parse_int(msg, varsz_p, vp->val.iptr)) == NULL) { 704 free(vp->val.iptr); 705 return (NULL); 706 } 707 vp->val_len = sizeof (int); 708 break; 709 710 case ASN_COUNTER: 711 case ASN_TIMETICKS: 712 vp->val.uiptr = (uint_t *)calloc(1, sizeof (uint_t)); 713 if (vp->val.uiptr == NULL) 714 return (NULL); 715 716 if ((p = asn_parse_uint(msg, varsz_p, vp->val.uiptr)) == NULL) { 717 free(vp->val.uiptr); 718 return (NULL); 719 } 720 vp->val_len = sizeof (uint_t); 721 vp->type = msg[0]; 722 break; 723 724 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR: 725 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR: 726 p = asn_parse_string(msg, varsz_p, &vp->val.str, &vp->val_len); 727 if (p == NULL) 728 return (NULL); 729 break; 730 731 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID: 732 p = asn_parse_objid(msg, varsz_p, &vp->val.objid, &n_subids); 733 if (p == NULL) 734 return (NULL); 735 vp->val_len = n_subids * sizeof (oid); 736 break; 737 738 case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_NULL: 739 case SNMP_NOSUCHOBJECT: 740 case SNMP_NOSUCHINSTANCE: 741 case SNMP_ENDOFMIBVIEW: 742 default: 743 p = asn_parse_length(msg + 1, &asnobj_len); 744 if (p == NULL) 745 return (NULL); 746 747 hdrlen = p - msg; 748 if (*varsz_p < (hdrlen + asnobj_len)) 749 return (NULL); 750 751 vp->type = msg[0]; 752 vp->val_len = asnobj_len; 753 754 *varsz_p -= (hdrlen + asnobj_len); 755 p += asnobj_len; 756 break; 757 } 758 759 return (p); 760 } 761