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