1 // Copyright (C) 2002 Microsoft Corporation 2 // All rights reserved. 3 // 4 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" 5 // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 6 // OR IMPLIED, INCLUDING BUT NOT LIMITED 7 // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY 8 // AND/OR FITNESS FOR A PARTICULAR PURPOSE. 9 // 10 // Date - 10/08/2002 11 // Author - Sanj Surati 12 13 14 ///////////////////////////////////////////////////////////// 15 // 16 // DERPARSE.C 17 // 18 // SPNEGO Token Handler Source File 19 // 20 // Contains implementation of ASN.1 DER read/write functions 21 // as defined in DERPARSE.H. 22 // 23 ///////////////////////////////////////////////////////////// 24 25 #include <stdlib.h> 26 #include <stdio.h> 27 #include <memory.h> 28 #include <sys/byteorder.h> 29 #include "spnego.h" 30 #include "derparse.h" 31 32 // 33 // The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in 34 // the array below, that a mechanism can be found. 35 // 36 37 #pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH) 38 MECH_OID g_stcMechOIDList [] = 39 { 40 {(unsigned char *)"\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02", 41 11, 9, spnego_mech_oid_Kerberos_V5_Legacy}, // 1.2.840.48018.1.2.2 42 {(unsigned char *)"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 43 11, 9, spnego_mech_oid_Kerberos_V5}, // 1.2.840.113554.1.2.2 44 {(unsigned char *)"\x06\x06\x2b\x06\x01\x05\x05\x02", 45 8, 6, spnego_mech_oid_Spnego}, // 1.3.6.1.5.5.2 46 {(unsigned char *)"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a", 47 12, 10, spnego_mech_oid_NTLMSSP}, // 1.3.6.1.4.1.311.2.2.10 48 {(unsigned char *)"", 0, 0, spnego_mech_oid_NotUsed // Placeholder 49 } 50 }; 51 #pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH) 52 53 ///////////////////////////////////////////////////////////////////////////// 54 // 55 // Function: 56 // ASNDerGetLength 57 // 58 // Parameters: 59 // [in] pbLengthData - DER Length Data 60 // [in] nBoundaryLength - Length that value must not exceed. 61 // [out] pnLength - Filled out with length value 62 // [out] pnNumLengthBytes - Filled out with number of bytes 63 // consumed by DER length. 64 // 65 // Returns: 66 // int Success - SPNEGO_E_SUCCESS 67 // Failure - SPNEGO API Error code 68 // 69 // Comments : 70 // Interprets the data at pbLengthData as a DER length. The length must 71 // fit within the bounds of nBoundary length. We do not currently 72 // process lengths that take more than 4 bytes. 73 // 74 //////////////////////////////////////////////////////////////////////////// 75 76 int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength, 77 long* pnNumLengthBytes ) 78 { 79 int nReturn = SPNEGO_E_INVALID_LENGTH; 80 int nNumLengthBytes = 0; 81 82 // First check if the extended length bit is set 83 84 if ( *pbLengthData & LEN_XTND ) 85 { 86 // Lower 7 bits contain the number of trailing bytes that describe the length 87 nNumLengthBytes = *pbLengthData & LEN_MASK; 88 89 // Check that the number of bytes we are about to read is within our boundary 90 // constraints 91 92 if ( nNumLengthBytes <= nBoundaryLength - 1 ) 93 { 94 95 // For now, our handler won't deal with lengths greater than 4 bytes 96 if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 ) 97 { 98 // 0 out the initial length 99 *pnLength = 0L; 100 101 // Bump by 1 byte 102 pbLengthData++; 103 104 #ifdef _LITTLE_ENDIAN 105 106 // There may be a cleaner way to do this, but for now, this seems to be 107 // an easy way to do the transformation 108 switch ( nNumLengthBytes ) 109 { 110 case 1: 111 { 112 *( ( (unsigned char*) pnLength ) ) = *pbLengthData; 113 break; 114 } 115 116 case 2: 117 { 118 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1); 119 *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData); 120 121 break; 122 } 123 124 case 3: 125 { 126 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2); 127 *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); 128 *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); 129 break; 130 } 131 132 case 4: 133 { 134 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3); 135 *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2); 136 *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1); 137 *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData); 138 break; 139 } 140 141 } // SWITCH ( nNumLengthBytes ) 142 143 #else 144 // We are Big-Endian, so the length can be copied in from the source 145 // as is. Ensure that we adjust for the number of bytes we actually 146 // copy. 147 148 memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ), 149 pbLengthData, nNumLengthBytes ); 150 #endif 151 152 // Account for the initial length byte 153 *pnNumLengthBytes = nNumLengthBytes + 1; 154 nReturn = SPNEGO_E_SUCCESS; 155 156 } // IF Valid Length 157 158 } // IF num bytes to read is within the boundary length 159 160 } // IF xtended length 161 else 162 { 163 164 // Extended bit is not set, so the length is in the value and the one 165 // byte describes the length 166 *pnLength = *pbLengthData & LEN_MASK; 167 *pnNumLengthBytes = 1; 168 nReturn = SPNEGO_E_SUCCESS; 169 170 } 171 172 return nReturn; 173 } 174 175 176 ///////////////////////////////////////////////////////////////////////////// 177 // 178 // Function: 179 // ASNDerCheckToken 180 // 181 // Parameters: 182 // [in] pbTokenData - Token Data 183 // [in] nToken - Token identifier to check for 184 // [in] nLengthWithToken - Expected token length (with data) 185 // [in] nBoundaryLength - Length that value must not exceed. 186 // [out] pnLength - Filled out with data length 187 // [out] pnTokenLength - Filled out with number of bytes 188 // consumed by token identifier and length. 189 // 190 // Returns: 191 // int Success - SPNEGO_E_SUCCESS 192 // Failure - SPNEGO API Error code 193 // 194 // Comments : 195 // Checks the data pointed to by pbTokenData for the specified token 196 // identifier and the length that immediately follows. If 197 // nLengthWithToken is > 0, the calculated length must match. The 198 // length must also not exceed the specified boundary length . 199 // 200 //////////////////////////////////////////////////////////////////////////// 201 202 int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken, 203 long nLengthWithToken, long nBoundaryLength, 204 long* pnLength, long* pnTokenLength ) 205 { 206 207 int nReturn = SPNEGO_E_INVALID_LENGTH; 208 long nNumLengthBytes = 0L; 209 210 // Make sure that we've at least got 2 bytes of room to work with 211 212 if ( nBoundaryLength >= 2 ) 213 { 214 // The first byte of the token data MUST match the specified token 215 if ( *pbTokenData == nToken ) 216 { 217 // Next byte indicates the length 218 pbTokenData++; 219 220 // Get the length described by the token 221 if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength, 222 &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS ) 223 { 224 // Verify that the length is LESS THAN the boundary length 225 // (this should prevent us walking out of our buffer) 226 if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) ) 227 { 228 229 nReturn = SPNEGO_E_INVALID_LENGTH; 230 231 } 232 233 // If we were passed a length to check, do so now 234 if ( nLengthWithToken > 0L ) 235 { 236 237 // Check that the expected length matches 238 if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength ) 239 { 240 241 nReturn = SPNEGO_E_INVALID_LENGTH; 242 243 } 244 245 } // IF need to validate length 246 247 if ( SPNEGO_E_SUCCESS == nReturn ) 248 { 249 *pnTokenLength = nNumLengthBytes + 1; 250 } 251 252 } // IF ASNDerGetLength 253 254 } // IF token matches 255 else 256 { 257 nReturn = SPNEGO_E_TOKEN_NOT_FOUND; 258 } 259 260 } // IF Boundary Length is at least 2 bytes 261 262 return nReturn; 263 } 264 265 ///////////////////////////////////////////////////////////////////////////// 266 // 267 // Function: 268 // ASNDerCheckOID 269 // 270 // Parameters: 271 // [in] pbTokenData - Token Data 272 // [in] nMechOID - OID we are looking for 273 // [in] nBoundaryLength - Length that value must not exceed. 274 // [out] pnTokenLength - Filled out with number of bytes 275 // consumed by token and data. 276 // 277 // Returns: 278 // int Success - SPNEGO_E_SUCCESS 279 // Failure - SPNEGO API Error code 280 // 281 // Comments : 282 // Checks the data pointed to by pbTokenData for the specified OID. 283 // 284 //////////////////////////////////////////////////////////////////////////// 285 286 int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength, 287 long* pnTokenLength ) 288 { 289 int nReturn = 0L; 290 long nLength = 0L; 291 292 // Verify that we have an OID token 293 if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength, 294 &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS ) 295 { 296 // Add the data length to the Token Length 297 *pnTokenLength += nLength; 298 299 // Token Lengths plus the actual length must match the length in our OID list element. 300 // If it doesn't, we're done 301 if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen ) 302 { 303 // Memcompare the token and the expected field 304 if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 ) 305 { 306 nReturn = SPNEGO_E_UNEXPECTED_OID; 307 } 308 } 309 else 310 { 311 nReturn = SPNEGO_E_UNEXPECTED_OID; 312 } 313 314 } // IF OID Token CHecks 315 316 return nReturn; 317 } 318 319 ///////////////////////////////////////////////////////////////////////////// 320 // 321 // Function: 322 // ASNDerCalcNumLengthBytes 323 // 324 // Parameters: 325 // [in] nLength - Length to calculate length bytes for. 326 // 327 // Returns: 328 // int Number of bytes necessary to represent length 329 // 330 // Comments : 331 // Helper function to calculate the number of length bytes necessary to 332 // represent a length value. For our purposes, a 32-bit value should be 333 // enough to describea length. 334 // 335 //////////////////////////////////////////////////////////////////////////// 336 337 int ASNDerCalcNumLengthBytes( long nLength ) 338 { 339 if ( nLength <= 0x7F ) 340 { 341 // A single byte will be sufficient for describing this length. 342 // The byte will simply contain the length 343 return 1; 344 } 345 else if ( nLength <= 0xFF ) 346 { 347 // Two bytes are necessary, one to say how many following bytes 348 // describe the length, and one to give the length 349 return 2; 350 } 351 else if ( nLength <= 0xFFFF ) 352 { 353 // Three bytes are necessary, one to say how many following bytes 354 // describe the length, and two to give the length 355 return 3; 356 } 357 else if ( nLength <= 0xFFFFFF ) 358 { 359 // Four bytes are necessary, one to say how many following bytes 360 // describe the length, and three to give the length 361 return 4; 362 } 363 else 364 { 365 // Five bytes are necessary, one to say how many following bytes 366 // describe the length, and four to give the length 367 return 5; 368 } 369 } 370 371 372 ///////////////////////////////////////////////////////////////////////////// 373 // 374 // Function: 375 // ASNDerCalcTokenLength 376 // 377 // Parameters: 378 // [in] nLength - Length to calculate length bytes for. 379 // [in] nDataLength - Actual Data length value. 380 // 381 // Returns: 382 // long Number of bytes necessary to represent a token, length and data 383 // 384 // Comments : 385 // Helper function to calculate a token and value size, based on a 386 // supplied length value, and any binary data that will need to be 387 // written out. 388 // 389 //////////////////////////////////////////////////////////////////////////// 390 391 long ASNDerCalcTokenLength( long nLength, long nDataLength ) 392 { 393 // Add a byte to the length size to account for a single byte to 394 // hold the token type. 395 long nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1; 396 397 return nTotalLength + nDataLength; 398 } 399 400 401 ///////////////////////////////////////////////////////////////////////////// 402 // 403 // Function: 404 // ASNDerCalcElementLength 405 // 406 // Parameters: 407 // [in] nDataLength - Length of data. 408 // [out] pnInternalLength - Filled out with length of element 409 // without sequence info. 410 // 411 // Returns: 412 // long Number of bytes necessary to represent an element 413 // 414 // Comments : 415 // Helper function to calculate an element length. An element consists 416 // of a sequence token, a type token and then the data. 417 // 418 //////////////////////////////////////////////////////////////////////////// 419 420 long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ) 421 { 422 // First the type token and the actual data 423 long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength ); 424 425 // Internal length is the length without the element sequence token 426 if ( NULL != pnInternalLength ) 427 { 428 *pnInternalLength = nTotalLength; 429 } 430 431 // Next add in the element's sequence token (remember that its 432 // length is the total length of the type token and data) 433 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 434 435 return nTotalLength; 436 } 437 438 ///////////////////////////////////////////////////////////////////////////// 439 // 440 // Function: 441 // ASNDerCalcMechListLength 442 // 443 // Parameters: 444 // [in] mechoid - Mech OID to put in list. 445 // [out] pnInternalLength - Filled out with length of element 446 // without the primary sequence token. 447 // 448 // Returns: 449 // long Number of bytes necessary to represent a mechList 450 // 451 // Comments : 452 // Helper function to calculate a MechList length. A mechlist consists 453 // of a NegTokenInit sequence token, a sequence token for the MechList 454 // and finally a list of OIDs. In our case, we only really have one 455 // OID. 456 // 457 //////////////////////////////////////////////////////////////////////////// 458 459 long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ) 460 { 461 // First the OID 462 long nTotalLength = g_stcMechOIDList[mechoid].iLen; 463 464 // Next add in a sequence token 465 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 466 467 // Internal length is the length without the element sequence token 468 if ( NULL != pnInternalLength ) 469 { 470 *pnInternalLength = nTotalLength; 471 } 472 473 // Finally add in the element's sequence token 474 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); 475 476 return nTotalLength; 477 } 478 479 480 ///////////////////////////////////////////////////////////////////////////// 481 // 482 // Function: 483 // ASNDerWriteLength 484 // 485 // Parameters: 486 // [out] pbData - Buffer to write into. 487 // [in] nLength - Length to write out. 488 // 489 // Returns: 490 // int Number of bytes written out 491 // 492 // Comments : 493 // Helper function to write out a length value following DER rules . 494 // 495 //////////////////////////////////////////////////////////////////////////// 496 497 int ASNDerWriteLength( unsigned char* pbData, long nLength ) 498 { 499 int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength ); 500 int nNumLengthBytes = nNumBytesRequired - 1; 501 502 503 if ( nNumBytesRequired > 1 ) 504 { 505 506 // Write out the number of bytes following which will be used 507 *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes ); 508 509 // Point to where we'll actually write the length 510 pbData++; 511 512 #ifdef _LITTLE_ENDIAN 513 514 // There may be a cleaner way to do this, but for now, this seems to be 515 // an easy way to do the transformation 516 switch ( nNumLengthBytes ) 517 { 518 case 1: 519 { 520 // Cast the length to a single byte, since we know that it 521 // is 0x7F or less (or we wouldn't only need a single byte). 522 523 *pbData = (unsigned char) nLength; 524 break; 525 } 526 527 case 2: 528 { 529 *pbData = *( ( (unsigned char*) &nLength ) + 1 ); 530 *( pbData + 1) = *( ( (unsigned char*) &nLength ) ); 531 break; 532 } 533 534 case 3: 535 { 536 *pbData = *( ( (unsigned char*) &nLength ) + 3 ); 537 *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); 538 *( pbData + 2) = *( ( (unsigned char*) &nLength ) ); 539 break; 540 } 541 542 case 4: 543 { 544 *pbData = *( ( (unsigned char*) &nLength ) + 3 ); 545 *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 ); 546 *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 ); 547 *( pbData + 3) = *( ( (unsigned char*) &nLength ) ); 548 break; 549 } 550 551 } // SWITCH ( nNumLengthBytes ) 552 553 #else 554 // We are Big-Endian, so the length can be copied in from the source 555 // as is. Ensure that we adjust for the number of bytes we actually 556 // copy. 557 558 memcpy( pbData, 559 ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes ); 560 #endif 561 562 } // IF > 1 byte for length 563 else 564 { 565 // Cast the length to a single byte, since we know that it 566 // is 0x7F or less (or we wouldn't only need a single byte). 567 568 *pbData = (unsigned char) nLength; 569 } 570 571 return nNumBytesRequired; 572 } 573 574 ///////////////////////////////////////////////////////////////////////////// 575 // 576 // Function: 577 // ASNDerWriteToken 578 // 579 // Parameters: 580 // [out] pbData - Buffer to write into. 581 // [in] ucType - Token Type 582 // [in] pbTokenValue - Actual Value 583 // [in] nLength - Length of Data. 584 // 585 // Returns: 586 // int Number of bytes written out 587 // 588 // Comments : 589 // Helper function to write out a token and any associated data. If 590 // pbTokenValue is non-NULL, then it is written out in addition to the 591 // token identifier and the length bytes. 592 // 593 //////////////////////////////////////////////////////////////////////////// 594 595 int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, 596 unsigned char* pbTokenValue, long nLength ) 597 { 598 int nTotalBytesWrittenOut = 0L; 599 int nNumLengthBytesWritten = 0L; 600 601 // Write out the type 602 *pbData = ucType; 603 604 // Wrote 1 byte, and move data pointer 605 nTotalBytesWrittenOut++; 606 pbData++; 607 608 // Now write out the length and adjust the number of bytes written out 609 nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength ); 610 611 nTotalBytesWrittenOut += nNumLengthBytesWritten; 612 pbData += nNumLengthBytesWritten; 613 614 // Write out the token value if we got one. The assumption is that the 615 // nLength value indicates how many bytes are in pbTokenValue. 616 617 if ( NULL != pbTokenValue ) 618 { 619 memcpy( pbData, pbTokenValue, nLength ); 620 nTotalBytesWrittenOut += nLength; 621 } 622 623 return nTotalBytesWrittenOut; 624 } 625 626 627 ///////////////////////////////////////////////////////////////////////////// 628 // 629 // Function: 630 // ASNDerWriteOID 631 // 632 // Parameters: 633 // [out] pbData - Buffer to write into. 634 // [in] eMechOID - OID to write out. 635 // 636 // Returns: 637 // int Number of bytes written out 638 // 639 // Comments : 640 // Helper function to write out an OID. For these we have the raw bytes 641 // listed in a global structure. The caller simply indicates which OID 642 // should be written and we will splat out the data. 643 // 644 //////////////////////////////////////////////////////////////////////////// 645 646 int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ) 647 { 648 649 memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen ); 650 651 return g_stcMechOIDList[eMechOID].iLen; 652 } 653 654 655 ///////////////////////////////////////////////////////////////////////////// 656 // 657 // Function: 658 // ASNDerWriteMechList 659 // 660 // Parameters: 661 // [out] pbData - Buffer to write into. 662 // [in] eMechOID - OID to put in MechList. 663 // 664 // Returns: 665 // int Number of bytes written out 666 // 667 // Comments : 668 // Helper function to write out a MechList. A MechList consists of the 669 // Init Token Sequence, a sequence token and then the list of OIDs. In 670 // our case the OID is from a global array of known OIDs. 671 // 672 //////////////////////////////////////////////////////////////////////////// 673 674 long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ) 675 { 676 // First get the length 677 long nInternalLength = 0L; 678 long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength ); 679 long nTempLength = 0L; 680 681 nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES, 682 NULL, nInternalLength ); 683 684 // Adjust the data pointer 685 pbData += nTempLength; 686 687 // Now write the Sequence token and the OID (the OID is a BLOB in the global 688 // structure. 689 690 nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE, 691 g_stcMechOIDList[mechoid].ucOid, 692 g_stcMechOIDList[mechoid].iLen ); 693 694 return nMechListLength; 695 } 696 697 698 ///////////////////////////////////////////////////////////////////////////// 699 // 700 // Function: 701 // ASNDerWriteElement 702 // 703 // Parameters: 704 // [out] pbData - Buffer to write into. 705 // [in] ucElementSequence - Sequence Token 706 // [in] ucType - Token Type 707 // [in] pbTokenValue - Actual Value 708 // [in] nLength - Length of Data. 709 // 710 // Returns: 711 // int Number of bytes written out 712 // 713 // Comments : 714 // Helper function to write out a SPNEGO Token element. An element 715 // consists of a sequence token, a type token and the associated data. 716 // 717 //////////////////////////////////////////////////////////////////////////// 718 719 int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence, 720 unsigned char ucType, unsigned char* pbTokenValue, long nLength ) 721 { 722 // First get the length 723 long nInternalLength = 0L; 724 long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength ); 725 long nTempLength = 0L; 726 727 // Write out the sequence byte and the length of the type and data 728 nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength ); 729 730 // Adjust the data pointer 731 pbData += nTempLength; 732 733 // Now write the type and the data. 734 nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength ); 735 736 return nElementLength; 737 } 738