This function sets the ciphers for use by a specified TLS object.\r
\r
@param[in] Tls Pointer to a TLS object.\r
- @param[in] CipherId Pointer to a UINT16 cipher Id.\r
+ @param[in] CipherId Array of UINT16 cipher identifiers. Each UINT16\r
+ cipher identifier comes from the TLS Cipher Suite\r
+ Registry of the IANA, interpreting Byte1 and Byte2\r
+ in network (big endian) byte order.\r
@param[in] CipherNum The number of cipher in the list.\r
\r
@retval EFI_SUCCESS The ciphers list was set successfully.\r
@retval EFI_INVALID_PARAMETER The parameter is invalid.\r
- @retval EFI_UNSUPPORTED Unsupported TLS cipher in the list.\r
+ @retval EFI_UNSUPPORTED No supported TLS cipher was found in CipherId.\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
\r
**/\r
EFI_STATUS\r
)\r
{\r
TLS_CONNECTION *TlsConn;\r
+ EFI_STATUS Status;\r
+ CONST TLS_CIPHER_MAPPING **MappedCipher;\r
+ UINTN MappedCipherBytes;\r
+ UINTN MappedCipherCount;\r
+ UINTN CipherStringSize;\r
UINTN Index;\r
CONST TLS_CIPHER_MAPPING *Mapping;\r
- CONST CHAR8 *MappingName;\r
- CHAR8 CipherString[500];\r
+ CHAR8 *CipherString;\r
+ CHAR8 *CipherStringPosition;\r
\r
TlsConn = (TLS_CONNECTION *) Tls;\r
if (TlsConn == NULL || TlsConn->Ssl == NULL || CipherId == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
- Mapping = NULL;\r
- MappingName = NULL;\r
-\r
- memset (CipherString, 0, sizeof (CipherString));\r
+ //\r
+ // Allocate the MappedCipher array for recording the mappings that we find\r
+ // for the input IANA identifiers in CipherId.\r
+ //\r
+ Status = SafeUintnMult (CipherNum, sizeof (*MappedCipher),\r
+ &MappedCipherBytes);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ MappedCipher = AllocatePool (MappedCipherBytes);\r
+ if (MappedCipher == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
\r
+ //\r
+ // Map the cipher IDs, and count the number of bytes for the full\r
+ // CipherString.\r
+ //\r
+ MappedCipherCount = 0;\r
+ CipherStringSize = 0;\r
for (Index = 0; Index < CipherNum; Index++) {\r
//\r
- // Handling OpenSSL / RFC Cipher name mapping.\r
+ // Look up the IANA-to-OpenSSL mapping.\r
//\r
- Mapping = TlsGetCipherMapping (*(CipherId + Index));\r
+ Mapping = TlsGetCipherMapping (CipherId[Index]);\r
if (Mapping == NULL) {\r
- return EFI_UNSUPPORTED;\r
- }\r
- MappingName = Mapping->OpensslCipher;\r
-\r
- if (Index != 0) {\r
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: skipping CipherId=0x%04x\n",\r
+ gEfiCallerBaseName, __FUNCTION__, CipherId[Index]));\r
//\r
- // The ciphers were separated by a colon.\r
+ // Skipping the cipher is valid because CipherId is an ordered\r
+ // preference list of ciphers, thus we can filter it as long as we\r
+ // don't change the relative order of elements on it.\r
//\r
- AsciiStrCatS (CipherString, sizeof (CipherString), ":");\r
+ continue;\r
}\r
+ //\r
+ // Accumulate Mapping->OpensslCipherLength into CipherStringSize. If this\r
+ // is not the first successful mapping, account for a colon (":") prefix\r
+ // too.\r
+ //\r
+ if (MappedCipherCount > 0) {\r
+ Status = SafeUintnAdd (CipherStringSize, 1, &CipherStringSize);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
+ }\r
+ }\r
+ Status = SafeUintnAdd (CipherStringSize, Mapping->OpensslCipherLength,\r
+ &CipherStringSize);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
+ }\r
+ //\r
+ // Record the mapping.\r
+ //\r
+ MappedCipher[MappedCipherCount++] = Mapping;\r
+ }\r
+\r
+ //\r
+ // Verify that at least one IANA cipher ID could be mapped; account for the\r
+ // terminating NUL character in CipherStringSize; allocate CipherString.\r
+ //\r
+ if (MappedCipherCount == 0) {\r
+ DEBUG ((DEBUG_ERROR, "%a:%a: no CipherId could be mapped\n",\r
+ gEfiCallerBaseName, __FUNCTION__));\r
+ Status = EFI_UNSUPPORTED;\r
+ goto FreeMappedCipher;\r
+ }\r
+ Status = SafeUintnAdd (CipherStringSize, 1, &CipherStringSize);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
+ }\r
+ CipherString = AllocatePool (CipherStringSize);\r
+ if (CipherString == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
+ }\r
\r
- AsciiStrCatS (CipherString, sizeof (CipherString), MappingName);\r
+ //\r
+ // Go over the collected mappings and populate CipherString.\r
+ //\r
+ CipherStringPosition = CipherString;\r
+ for (Index = 0; Index < MappedCipherCount; Index++) {\r
+ Mapping = MappedCipher[Index];\r
+ //\r
+ // Append the colon (":") prefix except for the first mapping, then append\r
+ // Mapping->OpensslCipher.\r
+ //\r
+ if (Index > 0) {\r
+ *(CipherStringPosition++) = ':';\r
+ }\r
+ CopyMem (CipherStringPosition, Mapping->OpensslCipher,\r
+ Mapping->OpensslCipherLength);\r
+ CipherStringPosition += Mapping->OpensslCipherLength;\r
}\r
\r
- AsciiStrCatS (CipherString, sizeof (CipherString), ":@STRENGTH");\r
+ //\r
+ // NUL-terminate CipherString.\r
+ //\r
+ *(CipherStringPosition++) = '\0';\r
+ ASSERT (CipherStringPosition == CipherString + CipherStringSize);\r
+\r
+ //\r
+ // Log CipherString for debugging. CipherString can be very long if the\r
+ // caller provided a large CipherId array, so log CipherString in segments of\r
+ // 79 non-newline characters. (MAX_DEBUG_MESSAGE_LENGTH is usually 0x100 in\r
+ // DebugLib instances.)\r
+ //\r
+ DEBUG_CODE (\r
+ UINTN FullLength;\r
+ UINTN SegmentLength;\r
+\r
+ FullLength = CipherStringSize - 1;\r
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: CipherString={\n", gEfiCallerBaseName,\r
+ __FUNCTION__));\r
+ for (CipherStringPosition = CipherString;\r
+ CipherStringPosition < CipherString + FullLength;\r
+ CipherStringPosition += SegmentLength) {\r
+ SegmentLength = FullLength - (CipherStringPosition - CipherString);\r
+ if (SegmentLength > 79) {\r
+ SegmentLength = 79;\r
+ }\r
+ DEBUG ((DEBUG_VERBOSE, "%.*a\n", SegmentLength, CipherStringPosition));\r
+ }\r
+ DEBUG ((DEBUG_VERBOSE, "}\n"));\r
+ //\r
+ // Restore the pre-debug value of CipherStringPosition by skipping over the\r
+ // trailing NUL.\r
+ //\r
+ CipherStringPosition++;\r
+ ASSERT (CipherStringPosition == CipherString + CipherStringSize);\r
+ );\r
\r
//\r
// Sets the ciphers for use by the Tls object.\r
//\r
if (SSL_set_cipher_list (TlsConn->Ssl, CipherString) <= 0) {\r
- return EFI_UNSUPPORTED;\r
+ Status = EFI_UNSUPPORTED;\r
+ goto FreeCipherString;\r
}\r
\r
- return EFI_SUCCESS;\r
+ Status = EFI_SUCCESS;\r
+\r
+FreeCipherString:\r
+ FreePool (CipherString);\r
+\r
+FreeMappedCipher:\r
+ FreePool (MappedCipher);\r
+\r
+ return Status;\r
}\r
\r
/**\r