1 /** @file 2 Implementation of EFI TLS Protocol Interfaces. 3 4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> 5 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php. 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 16 #include "TlsImpl.h" 17 18 EFI_TLS_PROTOCOL mTlsProtocol = { 19 TlsSetSessionData, 20 TlsGetSessionData, 21 TlsBuildResponsePacket, 22 TlsProcessPacket 23 }; 24 25 /** 26 Set TLS session data. 27 28 The SetSessionData() function set data for a new TLS session. All session data should 29 be set before BuildResponsePacket() invoked. 30 31 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance. 32 @param[in] DataType TLS session data type. 33 @param[in] Data Pointer to session data. 34 @param[in] DataSize Total size of session data. 35 36 @retval EFI_SUCCESS The TLS session data is set successfully. 37 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: 38 This is NULL. 39 Data is NULL. 40 DataSize is 0. 41 @retval EFI_UNSUPPORTED The DataType is unsupported. 42 @retval EFI_ACCESS_DENIED If the DataType is one of below: 43 EfiTlsClientRandom 44 EfiTlsServerRandom 45 EfiTlsKeyMaterial 46 @retval EFI_NOT_READY Current TLS session state is NOT 47 EfiTlsSessionStateNotStarted. 48 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. 49 **/ 50 EFI_STATUS 51 EFIAPI 52 TlsSetSessionData ( 53 IN EFI_TLS_PROTOCOL *This, 54 IN EFI_TLS_SESSION_DATA_TYPE DataType, 55 IN VOID *Data, 56 IN UINTN DataSize 57 ) 58 { 59 EFI_STATUS Status; 60 TLS_INSTANCE *Instance; 61 UINT16 *CipherId; 62 UINTN Index; 63 64 EFI_TPL OldTpl; 65 66 Status = EFI_SUCCESS; 67 CipherId = NULL; 68 69 if (This == NULL || Data == NULL || DataSize == 0) { 70 return EFI_INVALID_PARAMETER; 71 } 72 73 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 74 75 Instance = TLS_INSTANCE_FROM_PROTOCOL (This); 76 77 if (DataType != EfiTlsSessionState && Instance->TlsSessionState != EfiTlsSessionNotStarted){ 78 Status = EFI_NOT_READY; 79 goto ON_EXIT; 80 } 81 82 switch (DataType) { 83 // 84 // Session Configuration 85 // 86 case EfiTlsVersion: 87 if (DataSize != sizeof (EFI_TLS_VERSION)) { 88 Status = EFI_INVALID_PARAMETER; 89 goto ON_EXIT; 90 } 91 92 Status = TlsSetVersion (Instance->TlsConn, ((EFI_TLS_VERSION *) Data)->Major, ((EFI_TLS_VERSION *) Data)->Minor); 93 break; 94 case EfiTlsConnectionEnd: 95 if (DataSize != sizeof (EFI_TLS_CONNECTION_END)) { 96 Status = EFI_INVALID_PARAMETER; 97 goto ON_EXIT; 98 } 99 100 Status = TlsSetConnectionEnd (Instance->TlsConn, *((EFI_TLS_CONNECTION_END *) Data)); 101 break; 102 case EfiTlsCipherList: 103 CipherId = AllocatePool (DataSize); 104 if (CipherId == NULL) { 105 Status = EFI_OUT_OF_RESOURCES; 106 goto ON_EXIT; 107 } 108 109 for (Index = 0; Index < DataSize / sizeof (EFI_TLS_CIPHER); Index++) { 110 *(CipherId +Index) = HTONS (*(((UINT16 *) Data) + Index)); 111 } 112 113 Status = TlsSetCipherList (Instance->TlsConn, CipherId, DataSize / sizeof (EFI_TLS_CIPHER)); 114 115 FreePool (CipherId); 116 break; 117 case EfiTlsCompressionMethod: 118 // 119 // TLS seems only define one CompressionMethod.null, which specifies that data exchanged via the 120 // record protocol will not be compressed. 121 // More information from OpenSSL: http://www.openssl.org/docs/manmaster/ssl/SSL_COMP_add_compression_method.html 122 // The TLS RFC does however not specify compression methods or their corresponding identifiers, 123 // so there is currently no compatible way to integrate compression with unknown peers. 124 // It is therefore currently not recommended to integrate compression into applications. 125 // Applications for non-public use may agree on certain compression methods. 126 // Using different compression methods with the same identifier will lead to connection failure. 127 // 128 for (Index = 0; Index < DataSize / sizeof (EFI_TLS_COMPRESSION); Index++) { 129 Status = TlsSetCompressionMethod (*((UINT8 *) Data + Index)); 130 if (EFI_ERROR (Status)) { 131 break; 132 } 133 } 134 135 break; 136 case EfiTlsExtensionData: 137 Status = EFI_UNSUPPORTED; 138 goto ON_EXIT; 139 case EfiTlsVerifyMethod: 140 if (DataSize != sizeof (EFI_TLS_VERIFY)) { 141 Status = EFI_INVALID_PARAMETER; 142 goto ON_EXIT; 143 } 144 145 TlsSetVerify (Instance->TlsConn, *((UINT32 *) Data)); 146 break; 147 case EfiTlsSessionID: 148 if (DataSize != sizeof (EFI_TLS_SESSION_ID)) { 149 Status = EFI_INVALID_PARAMETER; 150 goto ON_EXIT; 151 } 152 153 Status = TlsSetSessionId ( 154 Instance->TlsConn, 155 ((EFI_TLS_SESSION_ID *) Data)->Data, 156 ((EFI_TLS_SESSION_ID *) Data)->Length 157 ); 158 break; 159 case EfiTlsSessionState: 160 if (DataSize != sizeof (EFI_TLS_SESSION_STATE)) { 161 Status = EFI_INVALID_PARAMETER; 162 goto ON_EXIT; 163 } 164 165 Instance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) Data; 166 break; 167 // 168 // Session information 169 // 170 case EfiTlsClientRandom: 171 Status = EFI_ACCESS_DENIED; 172 break; 173 case EfiTlsServerRandom: 174 Status = EFI_ACCESS_DENIED; 175 break; 176 case EfiTlsKeyMaterial: 177 Status = EFI_ACCESS_DENIED; 178 break; 179 // 180 // Unsupported type. 181 // 182 default: 183 Status = EFI_UNSUPPORTED; 184 } 185 186 ON_EXIT: 187 gBS->RestoreTPL (OldTpl); 188 return Status; 189 } 190 191 /** 192 Get TLS session data. 193 194 The GetSessionData() function return the TLS session information. 195 196 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance. 197 @param[in] DataType TLS session data type. 198 @param[in, out] Data Pointer to session data. 199 @param[in, out] DataSize Total size of session data. On input, it means 200 the size of Data buffer. On output, it means the size 201 of copied Data buffer if EFI_SUCCESS, and means the 202 size of desired Data buffer if EFI_BUFFER_TOO_SMALL. 203 204 @retval EFI_SUCCESS The TLS session data is got successfully. 205 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: 206 This is NULL. 207 DataSize is NULL. 208 Data is NULL if *DataSize is not zero. 209 @retval EFI_UNSUPPORTED The DataType is unsupported. 210 @retval EFI_NOT_FOUND The TLS session data is not found. 211 @retval EFI_NOT_READY The DataType is not ready in current session state. 212 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the data. 213 **/ 214 EFI_STATUS 215 EFIAPI 216 TlsGetSessionData ( 217 IN EFI_TLS_PROTOCOL *This, 218 IN EFI_TLS_SESSION_DATA_TYPE DataType, 219 IN OUT VOID *Data, OPTIONAL 220 IN OUT UINTN *DataSize 221 ) 222 { 223 EFI_STATUS Status; 224 TLS_INSTANCE *Instance; 225 226 EFI_TPL OldTpl; 227 228 Status = EFI_SUCCESS; 229 230 if (This == NULL || DataSize == NULL || (Data == NULL && *DataSize != 0)) { 231 return EFI_INVALID_PARAMETER; 232 } 233 234 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 235 236 Instance = TLS_INSTANCE_FROM_PROTOCOL (This); 237 238 if (Instance->TlsSessionState == EfiTlsSessionNotStarted && 239 (DataType == EfiTlsSessionID || DataType == EfiTlsClientRandom || 240 DataType == EfiTlsServerRandom || DataType == EfiTlsKeyMaterial)) { 241 Status = EFI_NOT_READY; 242 goto ON_EXIT; 243 } 244 245 switch (DataType) { 246 case EfiTlsVersion: 247 if (*DataSize < sizeof (EFI_TLS_VERSION)) { 248 *DataSize = sizeof (EFI_TLS_VERSION); 249 Status = EFI_BUFFER_TOO_SMALL; 250 goto ON_EXIT; 251 } 252 *DataSize = sizeof (EFI_TLS_VERSION); 253 *((UINT16 *) Data) = HTONS (TlsGetVersion (Instance->TlsConn)); 254 break; 255 case EfiTlsConnectionEnd: 256 if (*DataSize < sizeof (EFI_TLS_CONNECTION_END)) { 257 *DataSize = sizeof (EFI_TLS_CONNECTION_END); 258 Status = EFI_BUFFER_TOO_SMALL; 259 goto ON_EXIT; 260 } 261 *DataSize = sizeof (EFI_TLS_CONNECTION_END); 262 *((UINT8 *) Data) = TlsGetConnectionEnd (Instance->TlsConn); 263 break; 264 case EfiTlsCipherList: 265 // 266 // Get the current session cipher suite. 267 // 268 if (*DataSize < sizeof (EFI_TLS_CIPHER)) { 269 *DataSize = sizeof (EFI_TLS_CIPHER); 270 Status = EFI_BUFFER_TOO_SMALL; 271 goto ON_EXIT; 272 } 273 *DataSize = sizeof(EFI_TLS_CIPHER); 274 Status = TlsGetCurrentCipher (Instance->TlsConn, (UINT16 *) Data); 275 *((UINT16 *) Data) = HTONS (*((UINT16 *) Data)); 276 break; 277 case EfiTlsCompressionMethod: 278 // 279 // Get the current session compression method. 280 // 281 if (*DataSize < sizeof (EFI_TLS_COMPRESSION)) { 282 *DataSize = sizeof (EFI_TLS_COMPRESSION); 283 Status = EFI_BUFFER_TOO_SMALL; 284 goto ON_EXIT; 285 } 286 *DataSize = sizeof (EFI_TLS_COMPRESSION); 287 Status = TlsGetCurrentCompressionId (Instance->TlsConn, (UINT8 *) Data); 288 break; 289 case EfiTlsExtensionData: 290 Status = EFI_UNSUPPORTED; 291 goto ON_EXIT; 292 case EfiTlsVerifyMethod: 293 if (*DataSize < sizeof (EFI_TLS_VERIFY)) { 294 *DataSize = sizeof (EFI_TLS_VERIFY); 295 Status = EFI_BUFFER_TOO_SMALL; 296 goto ON_EXIT; 297 } 298 *DataSize = sizeof (EFI_TLS_VERIFY); 299 *((UINT32 *) Data) = TlsGetVerify (Instance->TlsConn); 300 break; 301 case EfiTlsSessionID: 302 if (*DataSize < sizeof (EFI_TLS_SESSION_ID)) { 303 *DataSize = sizeof (EFI_TLS_SESSION_ID); 304 Status = EFI_BUFFER_TOO_SMALL; 305 goto ON_EXIT; 306 } 307 *DataSize = sizeof (EFI_TLS_SESSION_ID); 308 Status = TlsGetSessionId ( 309 Instance->TlsConn, 310 ((EFI_TLS_SESSION_ID *) Data)->Data, 311 &(((EFI_TLS_SESSION_ID *) Data)->Length) 312 ); 313 break; 314 case EfiTlsSessionState: 315 if (*DataSize < sizeof (EFI_TLS_SESSION_STATE)) { 316 *DataSize = sizeof (EFI_TLS_SESSION_STATE); 317 Status = EFI_BUFFER_TOO_SMALL; 318 goto ON_EXIT; 319 } 320 *DataSize = sizeof (EFI_TLS_SESSION_STATE); 321 CopyMem (Data, &Instance->TlsSessionState, *DataSize); 322 break; 323 case EfiTlsClientRandom: 324 if (*DataSize < sizeof (EFI_TLS_RANDOM)) { 325 *DataSize = sizeof (EFI_TLS_RANDOM); 326 Status = EFI_BUFFER_TOO_SMALL; 327 goto ON_EXIT; 328 } 329 *DataSize = sizeof (EFI_TLS_RANDOM); 330 TlsGetClientRandom (Instance->TlsConn, (UINT8 *) Data); 331 break; 332 case EfiTlsServerRandom: 333 if (*DataSize < sizeof (EFI_TLS_RANDOM)) { 334 *DataSize = sizeof (EFI_TLS_RANDOM); 335 Status = EFI_BUFFER_TOO_SMALL; 336 goto ON_EXIT; 337 } 338 *DataSize = sizeof (EFI_TLS_RANDOM); 339 TlsGetServerRandom (Instance->TlsConn, (UINT8 *) Data); 340 break; 341 case EfiTlsKeyMaterial: 342 if (*DataSize < sizeof (EFI_TLS_MASTER_SECRET)) { 343 *DataSize = sizeof (EFI_TLS_MASTER_SECRET); 344 Status = EFI_BUFFER_TOO_SMALL; 345 goto ON_EXIT; 346 } 347 *DataSize = sizeof (EFI_TLS_MASTER_SECRET); 348 Status = TlsGetKeyMaterial (Instance->TlsConn, (UINT8 *) Data); 349 break; 350 // 351 // Unsupported type. 352 // 353 default: 354 Status = EFI_UNSUPPORTED; 355 } 356 357 ON_EXIT: 358 gBS->RestoreTPL (OldTpl); 359 return Status; 360 } 361 362 /** 363 Build response packet according to TLS state machine. This function is only valid for 364 alert, handshake and change_cipher_spec content type. 365 366 The BuildResponsePacket() function builds TLS response packet in response to the TLS 367 request packet specified by RequestBuffer and RequestSize. If RequestBuffer is NULL and 368 RequestSize is 0, and TLS session status is EfiTlsSessionNotStarted, the TLS session 369 will be initiated and the response packet needs to be ClientHello. If RequestBuffer is 370 NULL and RequestSize is 0, and TLS session status is EfiTlsSessionClosing, the TLS 371 session will be closed and response packet needs to be CloseNotify. If RequestBuffer is 372 NULL and RequestSize is 0, and TLS session status is EfiTlsSessionError, the TLS 373 session has errors and the response packet needs to be Alert message based on error 374 type. 375 376 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance. 377 @param[in] RequestBuffer Pointer to the most recently received TLS packet. NULL 378 means TLS need initiate the TLS session and response 379 packet need to be ClientHello. 380 @param[in] RequestSize Packet size in bytes for the most recently received TLS 381 packet. 0 is only valid when RequestBuffer is NULL. 382 @param[out] Buffer Pointer to the buffer to hold the built packet. 383 @param[in, out] BufferSize Pointer to the buffer size in bytes. On input, it is 384 the buffer size provided by the caller. On output, it 385 is the buffer size in fact needed to contain the 386 packet. 387 388 @retval EFI_SUCCESS The required TLS packet is built successfully. 389 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: 390 This is NULL. 391 RequestBuffer is NULL but RequestSize is NOT 0. 392 RequestSize is 0 but RequestBuffer is NOT NULL. 393 BufferSize is NULL. 394 Buffer is NULL if *BufferSize is not zero. 395 @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to hold the response packet. 396 @retval EFI_NOT_READY Current TLS session state is NOT ready to build 397 ResponsePacket. 398 @retval EFI_ABORTED Something wrong build response packet. 399 **/ 400 EFI_STATUS 401 EFIAPI 402 TlsBuildResponsePacket ( 403 IN EFI_TLS_PROTOCOL *This, 404 IN UINT8 *RequestBuffer, OPTIONAL 405 IN UINTN RequestSize, OPTIONAL 406 OUT UINT8 *Buffer, OPTIONAL 407 IN OUT UINTN *BufferSize 408 ) 409 { 410 EFI_STATUS Status; 411 TLS_INSTANCE *Instance; 412 EFI_TPL OldTpl; 413 414 Status = EFI_SUCCESS; 415 416 if ((This == NULL) || (BufferSize == NULL) || 417 (RequestBuffer == NULL && RequestSize != 0) || 418 (RequestBuffer != NULL && RequestSize == 0) || 419 (Buffer == NULL && *BufferSize !=0)) { 420 return EFI_INVALID_PARAMETER; 421 } 422 423 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 424 425 Instance = TLS_INSTANCE_FROM_PROTOCOL (This); 426 427 if(RequestBuffer == NULL && RequestSize == 0) { 428 switch (Instance->TlsSessionState) { 429 case EfiTlsSessionNotStarted: 430 // 431 // ClientHello. 432 // 433 Status = TlsDoHandshake ( 434 Instance->TlsConn, 435 NULL, 436 0, 437 Buffer, 438 BufferSize 439 ); 440 if (EFI_ERROR (Status)) { 441 goto ON_EXIT; 442 } 443 444 // 445 // *BufferSize should not be zero when ClientHello. 446 // 447 if (*BufferSize == 0) { 448 Status = EFI_ABORTED; 449 goto ON_EXIT; 450 } 451 452 Instance->TlsSessionState = EfiTlsSessionHandShaking; 453 454 break; 455 case EfiTlsSessionClosing: 456 // 457 // TLS session will be closed and response packet needs to be CloseNotify. 458 // 459 Status = TlsCloseNotify ( 460 Instance->TlsConn, 461 Buffer, 462 BufferSize 463 ); 464 if (EFI_ERROR (Status)) { 465 goto ON_EXIT; 466 } 467 468 // 469 // *BufferSize should not be zero when build CloseNotify message. 470 // 471 if (*BufferSize == 0) { 472 Status = EFI_ABORTED; 473 goto ON_EXIT; 474 } 475 476 break; 477 case EfiTlsSessionError: 478 // 479 // TLS session has errors and the response packet needs to be Alert 480 // message based on error type. 481 // 482 Status = TlsHandleAlert ( 483 Instance->TlsConn, 484 NULL, 485 0, 486 Buffer, 487 BufferSize 488 ); 489 if (EFI_ERROR (Status)) { 490 goto ON_EXIT; 491 } 492 493 break; 494 default: 495 // 496 // Current TLS session state is NOT ready to build ResponsePacket. 497 // 498 Status = EFI_NOT_READY; 499 } 500 } else { 501 // 502 // 1. Received packet may have multiple TLS record messages. 503 // 2. One TLS record message may have multiple handshake protocol. 504 // 3. Some errors may be happened in handshake. 505 // TlsDoHandshake() can handle all of those cases. 506 // 507 if (TlsInHandshake (Instance->TlsConn)) { 508 Status = TlsDoHandshake ( 509 Instance->TlsConn, 510 RequestBuffer, 511 RequestSize, 512 Buffer, 513 BufferSize 514 ); 515 if (EFI_ERROR (Status)) { 516 goto ON_EXIT; 517 } 518 519 if (!TlsInHandshake (Instance->TlsConn)) { 520 Instance->TlsSessionState = EfiTlsSessionDataTransferring; 521 } 522 } else { 523 // 524 // Must be alert message, Decrypt it and build the ResponsePacket. 525 // 526 ASSERT (((TLS_RECORD_HEADER *) RequestBuffer)->ContentType == TLS_CONTENT_TYPE_ALERT); 527 528 Status = TlsHandleAlert ( 529 Instance->TlsConn, 530 RequestBuffer, 531 RequestSize, 532 Buffer, 533 BufferSize 534 ); 535 if (EFI_ERROR (Status)) { 536 if (Status != EFI_BUFFER_TOO_SMALL) { 537 Instance->TlsSessionState = EfiTlsSessionError; 538 } 539 540 goto ON_EXIT; 541 } 542 } 543 } 544 545 ON_EXIT: 546 gBS->RestoreTPL (OldTpl); 547 return Status; 548 } 549 550 /** 551 Decrypt or encrypt TLS packet during session. This function is only valid after 552 session connected and for application_data content type. 553 554 The ProcessPacket () function process each inbound or outbound TLS APP packet. 555 556 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance. 557 @param[in, out] FragmentTable Pointer to a list of fragment. The caller will take 558 responsible to handle the original FragmentTable while 559 it may be reallocated in TLS driver. If CryptMode is 560 EfiTlsEncrypt, on input these fragments contain the TLS 561 header and plain text TLS APP payload; on output these 562 fragments contain the TLS header and cipher text TLS 563 APP payload. If CryptMode is EfiTlsDecrypt, on input 564 these fragments contain the TLS header and cipher text 565 TLS APP payload; on output these fragments contain the 566 TLS header and plain text TLS APP payload. 567 @param[in] FragmentCount Number of fragment. 568 @param[in] CryptMode Crypt mode. 569 570 @retval EFI_SUCCESS The operation completed successfully. 571 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: 572 This is NULL. 573 FragmentTable is NULL. 574 FragmentCount is NULL. 575 CryptoMode is invalid. 576 @retval EFI_NOT_READY Current TLS session state is NOT 577 EfiTlsSessionDataTransferring. 578 @retval EFI_ABORTED Something wrong decryption the message. TLS session 579 status will become EfiTlsSessionError. The caller need 580 call BuildResponsePacket() to generate Error Alert 581 message and send it out. 582 @retval EFI_OUT_OF_RESOURCES No enough resource to finish the operation. 583 **/ 584 EFI_STATUS 585 EFIAPI 586 TlsProcessPacket ( 587 IN EFI_TLS_PROTOCOL *This, 588 IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable, 589 IN UINT32 *FragmentCount, 590 IN EFI_TLS_CRYPT_MODE CryptMode 591 ) 592 { 593 EFI_STATUS Status; 594 TLS_INSTANCE *Instance; 595 596 EFI_TPL OldTpl; 597 598 Status = EFI_SUCCESS; 599 600 if (This == NULL || FragmentTable == NULL || FragmentCount == NULL) { 601 return EFI_INVALID_PARAMETER; 602 } 603 604 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 605 606 Instance = TLS_INSTANCE_FROM_PROTOCOL (This); 607 608 if (Instance->TlsSessionState != EfiTlsSessionDataTransferring) { 609 Status = EFI_NOT_READY; 610 goto ON_EXIT; 611 } 612 613 // 614 // Packet sent or received may have multiple TLS record messages (Application data type). 615 // So,on input these fragments contain the TLS header and TLS APP payload; 616 // on output these fragments also contain the TLS header and TLS APP payload. 617 // 618 switch (CryptMode) { 619 case EfiTlsEncrypt: 620 Status = TlsEncryptPacket (Instance, FragmentTable, FragmentCount); 621 break; 622 case EfiTlsDecrypt: 623 Status = TlsDecryptPacket (Instance, FragmentTable, FragmentCount); 624 break; 625 default: 626 return EFI_INVALID_PARAMETER; 627 } 628 629 ON_EXIT: 630 gBS->RestoreTPL (OldTpl); 631 return Status; 632 } 633