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