/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #include #include #if !BYO_CRYPTO namespace Aws { namespace Iot { static Crt::String AddToUsernameParameter( Crt::String currentUsername, Crt::String parameterValue, Crt::String parameterPreText) { Crt::String return_string = currentUsername; if (return_string.find("?") != Crt::String::npos) { return_string += "&"; } else { return_string += "?"; } if (parameterValue.find(parameterPreText) != Crt::String::npos) { return return_string + parameterValue; } else { return return_string + parameterPreText + parameterValue; } } static bool buildMqtt5FinalUsername( Crt::Optional customAuthConfig, Crt::String &username) { if (customAuthConfig.has_value()) { /* If we're using token-signing authentication, then all token properties must be set */ bool usingSigning = false; if (customAuthConfig->GetTokenValue().has_value() || customAuthConfig->GetTokenKeyName().has_value() || customAuthConfig->GetTokenSignature().has_value()) { usingSigning = true; if (!customAuthConfig->GetTokenValue().has_value() || !customAuthConfig->GetTokenKeyName().has_value() || !customAuthConfig->GetTokenSignature().has_value()) { return false; } } Crt::String usernameString = ""; if (!customAuthConfig->GetUsername().has_value()) { if (!username.empty()) { usernameString += username; } } else { usernameString += customAuthConfig->GetUsername().value(); } if (customAuthConfig->GetAuthorizerName().has_value()) { usernameString = AddToUsernameParameter( usernameString, customAuthConfig->GetAuthorizerName().value(), "x-amz-customauthorizer-name="); } if (usingSigning) { usernameString = AddToUsernameParameter( usernameString, customAuthConfig->GetTokenValue().value(), customAuthConfig->GetTokenKeyName().value() + "="); usernameString = AddToUsernameParameter( usernameString, customAuthConfig->GetTokenSignature().value(), "x-amz-customauthorizer-signature="); } username = usernameString; } return true; } /***************************************************** * * Mqtt5ClientOptionsBuilder * *****************************************************/ Mqtt5ClientBuilder::Mqtt5ClientBuilder(Crt::Allocator *allocator) noexcept : m_allocator(allocator), m_port(0), m_lastError(0), m_enableMetricsCollection(true) { m_options = new Crt::Mqtt5::Mqtt5ClientOptions(allocator); } Mqtt5ClientBuilder::Mqtt5ClientBuilder(int error, Crt::Allocator *allocator) noexcept : m_allocator(allocator), m_options(nullptr), m_lastError(error) { } Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsFromPath( const Crt::String hostName, const char *certPath, const char *pkeyPath, Crt::Allocator *allocator) noexcept { Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitClientWithMtls(certPath, pkeyPath, allocator); if (!result->m_tlsConnectionOptions.value()) { result->m_lastError = result->m_tlsConnectionOptions->LastError(); return result; } result->withHostName(hostName); return result; } Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsFromMemory( const Crt::String hostName, const Crt::ByteCursor &cert, const Crt::ByteCursor &pkey, Crt::Allocator *allocator) noexcept { Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitClientWithMtls(cert, pkey, allocator); if (!result->m_tlsConnectionOptions.value()) { result->m_lastError = result->m_tlsConnectionOptions->LastError(); return result; } result->withHostName(hostName); return result; } Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsPkcs11( const Crt::String hostName, const Crt::Io::TlsContextPkcs11Options &pkcs11Options, Crt::Allocator *allocator) noexcept { Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitClientWithMtlsPkcs11(pkcs11Options, allocator); if (!result->m_tlsConnectionOptions.value()) { result->m_lastError = result->m_tlsConnectionOptions->LastError(); return result; } result->withHostName(hostName); return result; } Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithWindowsCertStorePath( const Crt::String hostName, const char *windowsCertStorePath, Crt::Allocator *allocator) noexcept { Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitClientWithMtlsSystemPath(windowsCertStorePath, allocator); if (!result->m_tlsConnectionOptions.value()) { result->m_lastError = result->m_tlsConnectionOptions->LastError(); return result; } result->withHostName(hostName); return result; } Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithWebsocket( const Crt::String hostName, const WebsocketConfig &config, Crt::Allocator *allocator) noexcept { Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitDefaultClient(); result->withHostName(hostName); result->m_websocketConfig = config; return result; } Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithCustomAuthorizer( const Crt::String hostName, const Mqtt5CustomAuthConfig &customAuthConfig, Crt::Allocator *allocator) noexcept { Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitDefaultClient(); result->withHostName(hostName); result->WithCustomAuthorizer(customAuthConfig); return result; } Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithCustomAuthorizerWebsocket( const Crt::String hostName, const Mqtt5CustomAuthConfig &customAuthConfig, const WebsocketConfig &config, Crt::Allocator *allocator) noexcept { Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitDefaultClient(); result->withHostName(hostName); result->m_websocketConfig = config; result->WithCustomAuthorizer(customAuthConfig); return result; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withHostName(const Crt::String hostName) { m_options->withHostName(hostName); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withPort(uint16_t port) noexcept { m_port = port; return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithCertificateAuthority(const char *caPath) noexcept { if (m_tlsConnectionOptions) { if (!m_tlsConnectionOptions->OverrideDefaultTrustStore(nullptr, caPath)) { m_lastError = m_tlsConnectionOptions->LastError(); } } return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithCertificateAuthority(const Crt::ByteCursor &cert) noexcept { if (m_tlsConnectionOptions) { if (!m_tlsConnectionOptions->OverrideDefaultTrustStore(cert)) { m_lastError = m_tlsConnectionOptions->LastError(); } } return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withHttpProxyOptions( const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept { m_proxyOptions = proxyOptions; return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithCustomAuthorizer(const Iot::Mqtt5CustomAuthConfig &config) noexcept { m_customAuthConfig = config; return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withConnectOptions( std::shared_ptr packetConnect) noexcept { m_connectOptions = packetConnect; return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withSessionBehavior(ClientSessionBehaviorType sessionBehavior) noexcept { m_options->withSessionBehavior(sessionBehavior); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientExtendedValidationAndFlowControl( ClientExtendedValidationAndFlowControl clientExtendedValidationAndFlowControl) noexcept { m_options->withClientExtendedValidationAndFlowControl(clientExtendedValidationAndFlowControl); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withOfflineQueueBehavior( ClientOperationQueueBehaviorType operationQueueBehavior) noexcept { m_options->withAckTimeoutSeconds(operationQueueBehavior); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withReconnectOptions(ReconnectOptions reconnectOptions) noexcept { m_options->withReconnectOptions(reconnectOptions); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withPingTimeoutMs(uint32_t pingTimeoutMs) noexcept { m_options->withPingTimeoutMs(pingTimeoutMs); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withConnackTimeoutMs(uint32_t connackTimeoutMs) noexcept { m_options->withConnackTimeoutMs(connackTimeoutMs); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withAckTimeoutSeconds(uint32_t ackTimeoutSeconds) noexcept { m_options->withAckTimeoutSeconds(ackTimeoutSeconds); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithSdkName(const Crt::String &sdkName) { m_sdkName = sdkName; return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithSdkVersion(const Crt::String &sdkVersion) { m_sdkVersion = sdkVersion; return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientConnectionSuccessCallback( OnConnectionSuccessHandler callback) noexcept { m_options->withClientConnectionSuccessCallback(std::move(callback)); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientConnectionFailureCallback( OnConnectionFailureHandler callback) noexcept { m_options->withClientConnectionFailureCallback(std::move(callback)); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientDisconnectionCallback( OnDisconnectionHandler callback) noexcept { m_options->withClientDisconnectionCallback(std::move(callback)); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientStoppedCallback(OnStoppedHandler callback) noexcept { m_options->withClientStoppedCallback(std::move(callback)); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientAttemptingConnectCallback( OnAttemptingConnectHandler callback) noexcept { m_options->withClientAttemptingConnectCallback(std::move(callback)); return *this; } Mqtt5ClientBuilder &Mqtt5ClientBuilder::withPublishReceivedCallback(OnPublishReceivedHandler callback) noexcept { m_options->withPublishReceivedCallback(std::move(callback)); return *this; } std::shared_ptr Mqtt5ClientBuilder::Build() noexcept { if (m_lastError != 0) { return nullptr; } uint16_t port = m_port; if (!port) // port is default to 0 { if (m_websocketConfig || Crt::Io::TlsContextOptions::IsAlpnSupported()) { port = 443; } else { port = 8883; } } if (port == 443 && !m_websocketConfig && Crt::Io::TlsContextOptions::IsAlpnSupported() && !m_customAuthConfig.has_value()) { if (!m_tlsConnectionOptions->SetAlpnList("x-amzn-mqtt-ca")) { return nullptr; } } if (m_customAuthConfig.has_value()) { if (port != 443) { AWS_LOGF_WARN( AWS_LS_MQTT_GENERAL, "Attempting to connect to authorizer with unsupported port. Port is not 443..."); } if (!m_websocketConfig) { if (!m_tlsConnectionOptions->SetAlpnList("mqtt")) { return nullptr; } } } // add metrics string to username (if metrics enabled) if (m_enableMetricsCollection || m_customAuthConfig.has_value()) { Crt::String username = ""; if (m_connectOptions != nullptr) { if (m_connectOptions->getUsername().has_value()) username = m_connectOptions->getUsername().value(); } else { m_connectOptions = std::make_shared(m_allocator); } if (m_customAuthConfig.has_value()) { if (!buildMqtt5FinalUsername(m_customAuthConfig, username)) { AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "Failed to setup CustomAuthorizerConfig, please check if the parameters are set " "correctly."); return nullptr; } if (m_customAuthConfig->GetPassword().has_value()) { m_connectOptions->withPassword(m_customAuthConfig->GetPassword().value()); } } if (m_enableMetricsCollection) { username = AddToUsernameParameter(username, "SDK", m_sdkName); username = AddToUsernameParameter(username, "Version", m_sdkName); } m_connectOptions->withUserName(username); } auto tlsContext = Crt::Io::TlsContext(m_tlsConnectionOptions.value(), Crt::Io::TlsMode::CLIENT, m_allocator); if (!tlsContext) { return nullptr; } m_options->withPort(port).withTlsConnectionOptions(tlsContext.NewConnectionOptions()); if (m_connectOptions != nullptr) { m_options->withConnectOptions(m_connectOptions); } if (m_websocketConfig.has_value()) { auto websocketConfig = m_websocketConfig.value(); auto signerTransform = [websocketConfig]( std::shared_ptr req, const Crt::Mqtt::OnWebSocketHandshakeInterceptComplete &onComplete) { // it is only a very happy coincidence that these function signatures match. This is the callback // for signing to be complete. It invokes the callback for websocket handshake to be complete. auto signingComplete = [onComplete](const std::shared_ptr &req1, int errorCode) { onComplete(req1, errorCode); }; auto signerConfig = websocketConfig.CreateSigningConfigCb(); websocketConfig.Signer->SignRequest(req, *signerConfig, signingComplete); }; m_options->withWebsocketHandshakeTransformCallback(signerTransform); bool useWebsocketProxyOptions = m_websocketConfig->ProxyOptions.has_value() && !m_proxyOptions.has_value(); if (useWebsocketProxyOptions) { m_options->withHttpProxyOptions(m_websocketConfig->ProxyOptions.value()); } else if (m_proxyOptions.has_value()) { m_options->withHttpProxyOptions(m_proxyOptions.value()); } } return Crt::Mqtt5::Mqtt5Client::NewMqtt5Client(*m_options, m_allocator); } Aws::Iot::Mqtt5CustomAuthConfig::Mqtt5CustomAuthConfig(Crt::Allocator *allocator) noexcept : m_allocator(allocator) { AWS_ZERO_STRUCT(m_passwordStorage); } Aws::Iot::Mqtt5CustomAuthConfig::~Mqtt5CustomAuthConfig() { aws_byte_buf_clean_up(&m_passwordStorage); } Aws::Iot::Mqtt5CustomAuthConfig::Mqtt5CustomAuthConfig(const Mqtt5CustomAuthConfig &rhs) { if (&rhs != this) { m_allocator = rhs.m_allocator; if (rhs.m_authorizerName.has_value()) { m_authorizerName = rhs.m_authorizerName.value(); } if (rhs.m_tokenKeyName.has_value()) { m_tokenKeyName = rhs.m_tokenKeyName.value(); } if (rhs.m_tokenSignature.has_value()) { m_tokenSignature = rhs.m_tokenSignature.value(); } if (rhs.m_tokenValue.has_value()) { m_tokenValue = rhs.m_tokenValue.value(); } if (rhs.m_username.has_value()) { m_username = rhs.m_username.value(); } if (rhs.m_password.has_value()) { AWS_ZERO_STRUCT(m_passwordStorage); aws_byte_buf_init_copy_from_cursor(&m_passwordStorage, m_allocator, rhs.m_password.value()); m_password = aws_byte_cursor_from_buf(&m_passwordStorage); } } } Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::operator=(const Mqtt5CustomAuthConfig &rhs) { if (&rhs != this) { m_allocator = rhs.m_allocator; if (rhs.m_authorizerName.has_value()) { m_authorizerName = rhs.m_authorizerName.value(); } if (rhs.m_tokenKeyName.has_value()) { m_tokenKeyName = rhs.m_tokenKeyName.value(); } if (rhs.m_tokenSignature.has_value()) { m_tokenSignature = rhs.m_tokenSignature.value(); } if (rhs.m_tokenValue.has_value()) { m_tokenValue = rhs.m_tokenValue.value(); } if (rhs.m_username.has_value()) { m_username = rhs.m_username.value(); } if (rhs.m_password.has_value()) { aws_byte_buf_clean_up(&m_passwordStorage); AWS_ZERO_STRUCT(m_passwordStorage); aws_byte_buf_init_copy_from_cursor(&m_passwordStorage, m_allocator, rhs.m_password.value()); m_password = aws_byte_cursor_from_buf(&m_passwordStorage); } } return *this; } const Crt::Optional &Mqtt5CustomAuthConfig::GetAuthorizerName() { return m_authorizerName; } const Crt::Optional &Mqtt5CustomAuthConfig::GetUsername() { return m_username; } const Crt::Optional &Mqtt5CustomAuthConfig::GetPassword() { return m_password; } const Crt::Optional &Mqtt5CustomAuthConfig::GetTokenKeyName() { return m_tokenKeyName; } const Crt::Optional &Mqtt5CustomAuthConfig::GetTokenValue() { return m_tokenValue; } const Crt::Optional &Mqtt5CustomAuthConfig::GetTokenSignature() { return m_tokenSignature; } Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithAuthorizerName(Crt::String authName) { m_authorizerName = std::move(authName); return *this; } Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithUsername(Crt::String username) { m_username = std::move(username); return *this; } Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithPassword(Crt::ByteCursor password) { aws_byte_buf_clean_up(&m_passwordStorage); AWS_ZERO_STRUCT(m_passwordStorage); aws_byte_buf_init_copy_from_cursor(&m_passwordStorage, m_allocator, password); m_password = aws_byte_cursor_from_buf(&m_passwordStorage); return *this; } Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithTokenKeyName(Crt::String tokenKeyName) { m_tokenKeyName = std::move(tokenKeyName); return *this; } Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithTokenValue(Crt::String tokenValue) { m_tokenValue = std::move(tokenValue); return *this; } Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithTokenSignature(Crt::String tokenSignature) { m_tokenSignature = std::move(tokenSignature); return *this; } } // namespace Iot } // namespace Aws #endif // !BYO_CRYPTO