FreeRTOS+lwip+mbedTLS+aws-iot-core Problems with MQTT connections using ALPN

The following is a list of possible bugs.

Call hierarchy
connectToServerWithBackoffRetries()
SecureSocketsTransport_Connect()
establishConnect()
tlsSetup() <----(1)
SOCKETS_SetSockOpt() <----(2)(3)

(1) The size of char * is passed. This depends on the platform and is typically 4 or 8.
(2) In the caller, char * is passed, but it is assigned to char **.
(3) The loop is the number of times ctx->ulAlpnProtocolsCount minus 1 (the same value as xOptionLength).

While pSocketsConfig->pAlpnProtos is char *, we need to assign char **, and the char ** we assign must be an array with at least sizeof(char *) elements.
This is not a safe implementation and does not appear to be the intended one.
Could you please tell me how to properly specifiy or fix connetToServerWithBackoffRetries()?

Thanks

<mqtt_demo_mutual_auth.c>

... (omitted) ...
/**
 * @brief This is the ALPN (Application-Layer Protocol Negotiation) string
 * required by AWS IoT for password-based authentication using TCP port 443.
 *
 * @note OpenSSL requires that the protocol string passed to it for configuration
 * be encoded with the prefix of 8-bit length information of the string. Thus, the
 * 4 byte (0x04) length information is prefixed to the string.
 */
#define AWS_IOT_PASSWORD_ALPN           "\x04mqtt"

/**
 * @brief Length of password ALPN.
 */
#define AWS_IOT_PASSWORD_ALPN_LENGTH    ( ( uint16_t ) ( sizeof( AWS_IOT_PASSWORD_ALPN ) - 1 ) )

    ... (omitted) ...

static int connectToServerWithBackoffRetries( NetworkContext_t * pNetworkContext,
                                              MQTTContext_t * pMqttContext,
                                              bool * pClientSessionPresent,
                                              bool * pBrokerSessionPresent )
{
    ... (omitted) ...
    
    if( AWS_MQTT_PORT == 443 )
    {
        /* Pass the ALPN protocol name depending on the port being used.
         * Please see more details about the ALPN protocol for the AWS IoT MQTT
         * endpoint in the link below.
         * https://aws.amazon.com/blogs/iot/mqtt-with-tls-client-authentication-on-port-443-why-it-is-useful-and-how-it-works/
         *
         * For username and password based authentication in AWS IoT,
         * #AWS_IOT_PASSWORD_ALPN is used. More details can be found in the
         * link below.
         * https://docs.aws.amazon.com/iot/latest/developerguide/custom-authentication.html
         */
        #ifdef CLIENT_USERNAME
            SocketsConfig.pAlpnProtos = AWS_IOT_PASSWORD_ALPN;
            SocketsConfig.alpnProtosLen = AWS_IOT_PASSWORD_ALPN_LENGTH;
        #else
            SocketsConfig.pAlpnProtos = AWS_IOT_MQTT_ALPN;
            SocketsConfig.alpnProtosLen = AWS_IOT_MQTT_ALPN_LENGTH;
        #endif
    }
    
    ... (omitted) ...
    
	    xNetworkStatus = SecureSocketsTransport_Connect( pNetworkContext,
                                         &serverInfo,
                                         &SocketsConfig );
    
    ... (omitted) ...
    
    return returnStatus;
}

<transport_secure_sockets.c>

static int32_t tlsSetup( const SocketsConfig_t * pSocketsConfig,
                         Socket_t tcpSocket,
                         const char * pHostName,
                         size_t hostnameLength )
{
    ... (omitted) ...
    
    /* Set ALPN option. */
    if( ( secureSocketStatus == SOCKETS_ERROR_NONE ) && ( pSocketsConfig->pAlpnProtos != NULL ) )
    {
        secureSocketStatus = SOCKETS_SetSockOpt( tcpSocket,
                                                 0,
                                                 SOCKETS_SO_ALPN_PROTOCOLS,
                                                 pSocketsConfig->pAlpnProtos,
                                                 sizeof(pSocketsConfig->pAlpnProtos ) );    //<----(1)

        if( secureSocketStatus != ( int32_t ) SOCKETS_ERROR_NONE )
        {
            LogError( ( "Failed to set ALPN option socket.secureSocketStatus=%d", secureSocketStatus ) );
        }
    }
    ... (omitted) ...
    
    return secureSocketStatus;
}



<iot_secure_sockets.c>

int32_t SOCKETS_SetSockOpt( Socket_t xSocket,
                            int32_t lLevel,
                            int32_t lOptionName,
                            const void * pvOptionValue,
                            size_t xOptionLength )
{
    ss_ctx_t * ctx;
    int ret = 0;
    char ** ppcAlpnIn = ( char ** ) pvOptionValue;    //<----(2)
    size_t xLength = 0;
    uint32_t ulProtocol;
    
    ... (omitted) ...
    
        case SOCKETS_SO_ALPN_PROTOCOLS:

            /* Do not set the ALPN option if the socket is already connected. */
            if( ctx->status & SS_STATUS_CONNECTED )
            {
                return SOCKETS_EISCONN;
            }

            /* Allocate a sufficiently long array of pointers. */
            ctx->ulAlpnProtocolsCount = 1 + xOptionLength;

            if( NULL == ( ctx->ppcAlpnProtocols =
                              ( char ** ) pvPortMalloc( ctx->ulAlpnProtocolsCount *
                                                        sizeof( char * ) ) ) )
            {
                return SOCKETS_ENOMEM;
            }
            else
            {
                memset( ctx->ppcAlpnProtocols,
                        0x00,
                        ctx->ulAlpnProtocolsCount * sizeof( char * ) );
            }

            /* Copy each protocol string. */
            for( ulProtocol = 0; ( ulProtocol < ctx->ulAlpnProtocolsCount - 1 );    //<----(3)
                 ulProtocol++ )
            {
                xLength = strlen( ppcAlpnIn[ ulProtocol ] );

                if( NULL == ( ctx->ppcAlpnProtocols[ ulProtocol ] =
                                  ( char * ) pvPortMalloc( 1 + xLength ) ) )
                {
                    ctx->ppcAlpnProtocols[ ulProtocol ] = NULL;
                    return SOCKETS_ENOMEM;
                }
                else
                {
                    memcpy( ctx->ppcAlpnProtocols[ ulProtocol ],
                            ppcAlpnIn[ ulProtocol ],
                            xLength );
                    ctx->ppcAlpnProtocols[ ulProtocol ][ xLength ] = '\0';
                }
            }

            break;
    
    ... (omitted) ...
    
    return ret;
}

Thanks for your post - I need a little more direction to understand where to look in the code.

I’m looking at this project, but it is using FreeRTOS+TCP, not lwIP, so think I’m looking in the wrong place. In that project I see tlsSetup() called here, but it is not taking sizeof( char * ) as a parameter. Grateful if you can post a link to the call to tlsSetup() that has this problem.