935 lines
26 KiB
C
935 lines
26 KiB
C
/* mqttexample.c
|
|
*
|
|
* Copyright (C) 2006-2025 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfMQTT.
|
|
*
|
|
* wolfMQTT is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfMQTT is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
/* Include the autoconf generated config.h */
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "wolfmqtt/mqtt_client.h"
|
|
#include "mqttexample.h"
|
|
#include "mqttnet.h"
|
|
#include "mqttport.h"
|
|
|
|
|
|
/* locals */
|
|
static volatile word16 mPacketIdLast;
|
|
static const char* kDefTopicName = DEFAULT_TOPIC_NAME;
|
|
static const char* kDefClientId = DEFAULT_CLIENT_ID;
|
|
|
|
/* argument parsing */
|
|
static int myoptind = 0;
|
|
static char* myoptarg = NULL;
|
|
|
|
#ifdef ENABLE_MQTT_TLS
|
|
#ifdef HAVE_SNI
|
|
static int useSNI;
|
|
static const char* mTlsSniHostName = NULL;
|
|
#endif
|
|
#ifdef HAVE_PQC
|
|
static const char* mTlsPQAlg = NULL;
|
|
#endif
|
|
#endif /* ENABLE_MQTT_TLS */
|
|
|
|
static int mygetopt(int argc, char** argv, const char* optstring)
|
|
{
|
|
static char* next = NULL;
|
|
|
|
char c;
|
|
char* cp;
|
|
|
|
if (myoptind == 0)
|
|
next = NULL; /* we're starting new/over */
|
|
|
|
if (next == NULL || *next == '\0') {
|
|
if (myoptind == 0)
|
|
myoptind++;
|
|
|
|
if (myoptind >= argc || argv[myoptind][0] != '-' ||
|
|
argv[myoptind][1] == '\0') {
|
|
myoptarg = NULL;
|
|
if (myoptind < argc)
|
|
myoptarg = argv[myoptind];
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (XSTRNCMP(argv[myoptind], "--", 2) == 0) {
|
|
myoptind++;
|
|
myoptarg = NULL;
|
|
|
|
if (myoptind < argc)
|
|
myoptarg = argv[myoptind];
|
|
|
|
return -1;
|
|
}
|
|
|
|
next = argv[myoptind];
|
|
next++; /* skip - */
|
|
myoptind++;
|
|
}
|
|
|
|
c = *next++;
|
|
/* The C++ strchr can return a different value */
|
|
cp = (char*)XSTRCHR(optstring, c);
|
|
|
|
if (cp == NULL || c == ':')
|
|
return '?';
|
|
|
|
cp++;
|
|
|
|
if (*cp == ':') {
|
|
if (*next != '\0') {
|
|
myoptarg = next;
|
|
next = NULL;
|
|
}
|
|
else if (myoptind < argc) {
|
|
myoptarg = argv[myoptind];
|
|
myoptind++;
|
|
}
|
|
else
|
|
return '?';
|
|
}
|
|
else if (*cp == ';') {
|
|
myoptarg = (char*)"";
|
|
if (*next != '\0') {
|
|
myoptarg = next;
|
|
next = NULL;
|
|
}
|
|
else if (myoptind < argc) {
|
|
/* Check if next argument is not a parameter argument */
|
|
if (argv[myoptind] && argv[myoptind][0] != '-') {
|
|
myoptarg = argv[myoptind];
|
|
myoptind++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
/* used for testing only, requires wolfSSL RNG */
|
|
#ifdef ENABLE_MQTT_TLS
|
|
#include <wolfssl/wolfcrypt/random.h>
|
|
#endif
|
|
|
|
static int mqtt_get_rand(byte* data, word32 len)
|
|
{
|
|
int ret = -1;
|
|
#ifdef ENABLE_MQTT_TLS
|
|
WC_RNG rng;
|
|
ret = wc_InitRng(&rng);
|
|
if (ret == 0) {
|
|
ret = wc_RNG_GenerateBlock(&rng, data, len);
|
|
wc_FreeRng(&rng);
|
|
}
|
|
#elif defined(HAVE_RAND)
|
|
word32 i;
|
|
for (i = 0; i<len; i++) {
|
|
data[i] = (byte)rand();
|
|
}
|
|
ret = 0; /* success */
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
int mqtt_fill_random_hexstr(char* buf, word32 bufLen)
|
|
{
|
|
int rc = 0;
|
|
word32 pos = 0, sz, i;
|
|
const char kHexChar[] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
|
byte rndBytes[32]; /* fill up to x bytes at a time */
|
|
|
|
while (rc == 0 && pos < bufLen) {
|
|
sz = bufLen - pos;
|
|
if (sz > (int)sizeof(rndBytes))
|
|
sz = (int)sizeof(rndBytes);
|
|
sz /= 2; /* 1 byte expands to 2 bytes */
|
|
|
|
rc = mqtt_get_rand(rndBytes, sz);
|
|
if (rc == 0) {
|
|
/* Convert random to hex string */
|
|
for (i=0; i<sz; i++) {
|
|
byte in = rndBytes[i];
|
|
buf[pos + (i*2)] = kHexChar[in >> 4];
|
|
buf[pos + (i*2)+1] = kHexChar[in & 0xf];
|
|
}
|
|
pos += sz*2;
|
|
}
|
|
else {
|
|
PRINTF("MQTT Fill Random Failed! %d", rc);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
#ifndef TEST_RAND_SZ
|
|
#define TEST_RAND_SZ 4
|
|
#endif
|
|
char* mqtt_append_random(const char* inStr, word32 inLen)
|
|
{
|
|
int rc = 0;
|
|
char *tmp;
|
|
|
|
tmp = (char*)WOLFMQTT_MALLOC(inLen + 1 + (TEST_RAND_SZ*2) + 1);
|
|
if (tmp == NULL) {
|
|
rc = MQTT_CODE_ERROR_MEMORY;
|
|
}
|
|
if (rc == 0) {
|
|
/* Format: inStr + `_` randhex + null term */
|
|
XMEMCPY(tmp, inStr, inLen);
|
|
tmp[inLen] = '_';
|
|
rc = mqtt_fill_random_hexstr(tmp + inLen + 1, (TEST_RAND_SZ*2));
|
|
tmp[inLen + 1 + (TEST_RAND_SZ*2)] = '\0'; /* null term */
|
|
}
|
|
if (rc != 0) {
|
|
WOLFMQTT_FREE(tmp);
|
|
tmp = NULL;
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
void mqtt_show_usage(MQTTCtx* mqttCtx)
|
|
{
|
|
PRINTF("%s:", mqttCtx->app_name);
|
|
PRINTF("-? Help, print this usage");
|
|
PRINTF("-h <host> Host to connect to, default: %s",
|
|
mqttCtx->host);
|
|
#ifdef ENABLE_MQTT_TLS
|
|
PRINTF("-p <num> Port to connect on, default: Normal %d, TLS %d",
|
|
MQTT_DEFAULT_PORT, MQTT_SECURE_PORT);
|
|
PRINTF("-t Enable TLS"); /* Note: this string is used in test
|
|
* scripts to detect TLS feature */
|
|
PRINTF("-A <file> Load CA (validate peer)");
|
|
PRINTF("-K <key> Use private key (for TLS mutual auth)");
|
|
PRINTF("-c <cert> Use certificate (for TLS mutual auth)");
|
|
#ifndef ENABLE_MQTT_CURL
|
|
#ifdef HAVE_SNI
|
|
/* Remove SNI args for sn-client */
|
|
if(XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){
|
|
PRINTF("-S <str> Use Host Name Indication, blank defaults to host");
|
|
}
|
|
#endif /* HAVE_SNI */
|
|
#ifdef HAVE_PQC
|
|
PRINTF("-Q <str> Use Key Share with post-quantum algorithm");
|
|
#endif /* HAVE_PQC */
|
|
#endif /* !ENABLE_MQTT_CURL */
|
|
PRINTF("-p <num> Port to connect on, default: %d",
|
|
MQTT_DEFAULT_PORT);
|
|
#endif
|
|
PRINTF("-q <num> Qos Level 0-2, default: %d",
|
|
mqttCtx->qos);
|
|
PRINTF("-s Disable clean session connect flag");
|
|
PRINTF("-k <num> Keep alive seconds, default: %d",
|
|
mqttCtx->keep_alive_sec);
|
|
PRINTF("-i <id> Client Id, default: %s",
|
|
mqttCtx->client_id);
|
|
PRINTF("-l Enable LWT (Last Will and Testament)");
|
|
PRINTF("-u <str> Username");
|
|
PRINTF("-w <str> Password");
|
|
if (mqttCtx->message) {
|
|
/* Only mqttclient example can set message from CLI */
|
|
PRINTF("-m <str> Message, default: %s", mqttCtx->message);
|
|
}
|
|
PRINTF("-n <str> Topic name, default: %s", mqttCtx->topic_name);
|
|
PRINTF("-r Set Retain flag on publish message");
|
|
PRINTF("-C <num> Command Timeout, default: %dms",
|
|
mqttCtx->cmd_timeout_ms);
|
|
#ifdef WOLFMQTT_V5
|
|
PRINTF("-P <num> Max packet size the client will accept, default: %d",
|
|
DEFAULT_MAX_PKT_SZ);
|
|
#endif
|
|
PRINTF("-T Test mode");
|
|
PRINTF("-x Skip subscribe (for testing session persistence)");
|
|
PRINTF("-R <file> Ready file (touched when subscribed, for test sync)");
|
|
PRINTF("-f <file> Use file contents for publish");
|
|
if (!mqttCtx->debug_on) {
|
|
PRINTF("-d Enable example debug messages");
|
|
}
|
|
}
|
|
|
|
void mqtt_init_ctx(MQTTCtx* mqttCtx)
|
|
{
|
|
XMEMSET(mqttCtx, 0, sizeof(MQTTCtx));
|
|
mqttCtx->host = DEFAULT_MQTT_HOST;
|
|
mqttCtx->qos = DEFAULT_MQTT_QOS;
|
|
mqttCtx->clean_session = 1;
|
|
mqttCtx->keep_alive_sec = DEFAULT_KEEP_ALIVE_SEC;
|
|
mqttCtx->client_id = kDefClientId;
|
|
mqttCtx->topic_name = kDefTopicName;
|
|
mqttCtx->cmd_timeout_ms = DEFAULT_CMD_TIMEOUT_MS;
|
|
mqttCtx->debug_on = 1;
|
|
#ifdef WOLFMQTT_V5
|
|
mqttCtx->max_packet_size = DEFAULT_MAX_PKT_SZ;
|
|
mqttCtx->topic_alias = 1;
|
|
mqttCtx->topic_alias_max = 1;
|
|
#endif
|
|
#ifdef WOLFMQTT_DEFAULT_TLS
|
|
mqttCtx->use_tls = WOLFMQTT_DEFAULT_TLS;
|
|
#endif
|
|
#ifdef ENABLE_MQTT_TLS
|
|
mqttCtx->ca_file = NULL;
|
|
mqttCtx->mtls_keyfile = NULL;
|
|
mqttCtx->mtls_certfile = NULL;
|
|
#endif
|
|
mqttCtx->app_name = "mqttclient";
|
|
mqttCtx->message = DEFAULT_MESSAGE;
|
|
}
|
|
|
|
int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv)
|
|
{
|
|
int rc;
|
|
|
|
#ifdef ENABLE_MQTT_TLS
|
|
#ifdef ENABLE_MQTT_CURL
|
|
#define MQTT_TLS_ARGS "c:A:K:"
|
|
#else
|
|
#define MQTT_TLS_ARGS "c:A:K:S;Q:"
|
|
#endif
|
|
#else
|
|
#define MQTT_TLS_ARGS ""
|
|
#endif
|
|
#ifdef WOLFMQTT_V5
|
|
#define MQTT_V5_ARGS "P:"
|
|
#else
|
|
#define MQTT_V5_ARGS ""
|
|
#endif
|
|
|
|
while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rtdxR:" \
|
|
MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) {
|
|
switch ((char)rc) {
|
|
case '?' :
|
|
mqtt_show_usage(mqttCtx);
|
|
return MY_EX_USAGE;
|
|
|
|
case 'h' :
|
|
mqttCtx->host = myoptarg;
|
|
break;
|
|
|
|
case 'p' :
|
|
mqttCtx->port = (word16)XATOI(myoptarg);
|
|
if (mqttCtx->port == 0) {
|
|
return err_sys("Invalid Port Number!");
|
|
}
|
|
break;
|
|
|
|
case 'q' :
|
|
mqttCtx->qos = (MqttQoS)((byte)XATOI(myoptarg));
|
|
if (mqttCtx->qos > MQTT_QOS_2) {
|
|
return err_sys("Invalid QoS value!");
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
mqttCtx->clean_session = 0;
|
|
break;
|
|
|
|
case 'k':
|
|
mqttCtx->keep_alive_sec = XATOI(myoptarg);
|
|
break;
|
|
|
|
case 'i':
|
|
mqttCtx->client_id = myoptarg;
|
|
break;
|
|
|
|
case 'l':
|
|
mqttCtx->enable_lwt = 1;
|
|
break;
|
|
|
|
case 'u':
|
|
mqttCtx->username = myoptarg;
|
|
break;
|
|
|
|
case 'w':
|
|
mqttCtx->password = myoptarg;
|
|
break;
|
|
|
|
case 'm':
|
|
mqttCtx->message = myoptarg;
|
|
break;
|
|
|
|
case 'n':
|
|
mqttCtx->topic_name = myoptarg;
|
|
break;
|
|
|
|
case 'C':
|
|
mqttCtx->cmd_timeout_ms = XATOI(myoptarg);
|
|
break;
|
|
|
|
case 'T':
|
|
mqttCtx->test_mode = 1;
|
|
break;
|
|
|
|
case 'f':
|
|
mqttCtx->pub_file = myoptarg;
|
|
break;
|
|
|
|
case 'r':
|
|
mqttCtx->retain = 1;
|
|
break;
|
|
|
|
case 't':
|
|
mqttCtx->use_tls = 1;
|
|
break;
|
|
|
|
case 'd':
|
|
mqttCtx->debug_on = 1;
|
|
break;
|
|
|
|
case 'x':
|
|
mqttCtx->skip_subscribe = 1;
|
|
break;
|
|
|
|
case 'R':
|
|
mqttCtx->ready_file = myoptarg;
|
|
break;
|
|
|
|
#ifdef ENABLE_MQTT_TLS
|
|
case 'A':
|
|
mqttCtx->ca_file = myoptarg;
|
|
break;
|
|
case 'c':
|
|
mqttCtx->mtls_certfile = myoptarg;
|
|
break;
|
|
case 'K':
|
|
mqttCtx->mtls_keyfile = myoptarg;
|
|
break;
|
|
#ifndef ENABLE_MQTT_CURL
|
|
case 'S':
|
|
#ifdef HAVE_SNI
|
|
useSNI = 1;
|
|
mTlsSniHostName = myoptarg;
|
|
#else
|
|
PRINTF("To use '-S', enable SNI in wolfSSL");
|
|
#endif
|
|
break;
|
|
case 'Q':
|
|
#ifdef HAVE_PQC
|
|
mTlsPQAlg = myoptarg;
|
|
#else
|
|
PRINTF("To use '-Q', build wolfSSL with --enable-mlkem --enable-dilithium");
|
|
#endif
|
|
break;
|
|
#endif /* !ENABLE_MQTT_CURL */
|
|
#endif /* ENABLE_MQTT_TLS */
|
|
|
|
#ifdef WOLFMQTT_V5
|
|
case 'P':
|
|
mqttCtx->max_packet_size = XATOI(myoptarg);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
mqtt_show_usage(mqttCtx);
|
|
return MY_EX_USAGE;
|
|
}
|
|
|
|
/* Remove SNI functionality for sn-client */
|
|
if(!XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){
|
|
#ifdef HAVE_SNI
|
|
useSNI=0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
rc = 0;
|
|
myoptind = 0; /* reset for test cases */
|
|
|
|
/* if TLS not enable, check args */
|
|
#ifndef ENABLE_MQTT_TLS
|
|
if (mqttCtx->use_tls) {
|
|
PRINTF("Use TLS option not allowed (TLS not compiled in)");
|
|
mqttCtx->use_tls = 0;
|
|
if (mqttCtx->test_mode) {
|
|
return MY_EX_USAGE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SNI
|
|
if ((useSNI == 1) && (XSTRLEN(mTlsSniHostName) == 0)) {
|
|
/* Set SNI host name to host if -S was blank */
|
|
mTlsSniHostName = mqttCtx->host;
|
|
}
|
|
#endif
|
|
|
|
/* for test mode only */
|
|
/* add random data to end of client_id and topic_name */
|
|
if (mqttCtx->test_mode && mqttCtx->topic_name == kDefTopicName) {
|
|
char* topic_name = mqtt_append_random(kDefTopicName,
|
|
(word32)XSTRLEN(kDefTopicName));
|
|
if (topic_name) {
|
|
mqttCtx->topic_name = (const char*)topic_name;
|
|
mqttCtx->dynamicTopic = 1;
|
|
}
|
|
}
|
|
if (mqttCtx->test_mode && mqttCtx->client_id == kDefClientId) {
|
|
char* client_id = mqtt_append_random(kDefClientId,
|
|
(word32)XSTRLEN(kDefClientId));
|
|
if (client_id) {
|
|
mqttCtx->client_id = (const char*)client_id;
|
|
mqttCtx->dynamicClientId = 1;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void mqtt_free_ctx(MQTTCtx* mqttCtx)
|
|
{
|
|
if (mqttCtx == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (mqttCtx->dynamicTopic && mqttCtx->topic_name) {
|
|
WOLFMQTT_FREE((char*)mqttCtx->topic_name);
|
|
mqttCtx->topic_name = NULL;
|
|
}
|
|
if (mqttCtx->dynamicClientId && mqttCtx->client_id) {
|
|
WOLFMQTT_FREE((char*)mqttCtx->client_id);
|
|
mqttCtx->client_id = NULL;
|
|
}
|
|
}
|
|
|
|
#if defined(__GNUC__) && !defined(NO_EXIT) && !defined(WOLFMQTT_ZEPHYR)
|
|
__attribute__ ((noreturn))
|
|
#endif
|
|
int err_sys(const char* msg)
|
|
{
|
|
if (msg) {
|
|
PRINTF("wolfMQTT error: %s", msg);
|
|
}
|
|
exit(EXIT_FAILURE);
|
|
#ifdef WOLFMQTT_ZEPHYR
|
|
/* Zephyr compiler produces below warning. Let's silence it.
|
|
* warning: 'noreturn' function does return
|
|
* 477 | }
|
|
* | ^
|
|
*/
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
word16 mqtt_get_packetid(void)
|
|
{
|
|
/* Check rollover */
|
|
if (mPacketIdLast >= MAX_PACKET_ID) {
|
|
mPacketIdLast = 0;
|
|
}
|
|
|
|
return ++mPacketIdLast;
|
|
}
|
|
|
|
#ifdef WOLFMQTT_NONBLOCK
|
|
#if defined(MICROCHIP_MPLAB_HARMONY)
|
|
#include <system/tmr/sys_tmr.h>
|
|
#else
|
|
#include <time.h>
|
|
#endif
|
|
|
|
static word32 mqtt_get_timer_seconds(void)
|
|
{
|
|
word32 timer_sec = 0;
|
|
|
|
#if defined(MICROCHIP_MPLAB_HARMONY)
|
|
timer_sec = (word32)(SYS_TMR_TickCountGet() /
|
|
SYS_TMR_TickCounterFrequencyGet());
|
|
#else
|
|
/* Posix style time */
|
|
timer_sec = (word32)time(0);
|
|
#endif
|
|
|
|
return timer_sec;
|
|
}
|
|
|
|
int mqtt_check_timeout(int rc, word32* start_sec, word32 timeout_sec)
|
|
{
|
|
word32 elapsed_sec;
|
|
|
|
/* if start seconds not set or is not continue */
|
|
if (*start_sec == 0 || rc != MQTT_CODE_CONTINUE) {
|
|
*start_sec = mqtt_get_timer_seconds();
|
|
return rc;
|
|
}
|
|
|
|
/* Default to 2s timeout. This function sometimes incorrectly
|
|
* triggers if 1s is used because of rounding. */
|
|
if (timeout_sec == 0) {
|
|
timeout_sec = DEFAULT_CHK_TIMEOUT_S;
|
|
}
|
|
|
|
elapsed_sec = mqtt_get_timer_seconds();
|
|
if (*start_sec < elapsed_sec) {
|
|
elapsed_sec -= *start_sec;
|
|
if (elapsed_sec >= timeout_sec) {
|
|
*start_sec = mqtt_get_timer_seconds();
|
|
PRINTF("Timeout timer %d seconds", timeout_sec);
|
|
return MQTT_CODE_ERROR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
#endif /* WOLFMQTT_NONBLOCK */
|
|
|
|
|
|
#if defined(ENABLE_MQTT_TLS) && !defined(EXTERNAL_MQTT_TLS_CALLBACK)
|
|
|
|
#ifdef WOLFSSL_ENCRYPTED_KEYS
|
|
int mqtt_password_cb(char* passwd, int sz, int rw, void* userdata)
|
|
{
|
|
(void)rw;
|
|
(void)userdata;
|
|
if (userdata != NULL) {
|
|
XSTRNCPY(passwd, (char*)userdata, sz);
|
|
return (int)XSTRLEN((char*)userdata);
|
|
}
|
|
else {
|
|
XSTRNCPY(passwd, "yassl123", sz);
|
|
return (int)XSTRLEN(passwd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int mqtt_tls_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
char buffer[WOLFSSL_MAX_ERROR_SZ];
|
|
MQTTCtx *mqttCtx = NULL;
|
|
char appName[PRINT_BUFFER_SIZE] = {0};
|
|
|
|
if (store->userCtx != NULL) {
|
|
/* The client.ctx was stored during MqttSocket_Connect. */
|
|
mqttCtx = (MQTTCtx *)store->userCtx;
|
|
XSTRNCPY(appName, " for ", PRINT_BUFFER_SIZE-1);
|
|
XSTRNCAT(appName, mqttCtx->app_name,
|
|
PRINT_BUFFER_SIZE-XSTRLEN(appName)-1);
|
|
}
|
|
|
|
PRINTF("MQTT TLS Verify Callback%s: PreVerify %d, Error %d (%s)",
|
|
appName, preverify,
|
|
store->error, store->error != 0 ?
|
|
wolfSSL_ERR_error_string(store->error, buffer) : "none");
|
|
PRINTF(" Subject's domain name is %s", store->domain);
|
|
|
|
if (store->error != 0) {
|
|
/* Allowing to continue */
|
|
/* Should check certificate and return 0 if not okay */
|
|
PRINTF(" Allowing cert anyways");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Use this callback to setup TLS certificates and verify callbacks */
|
|
int mqtt_tls_cb(MqttClient* client)
|
|
{
|
|
int rc = WOLFSSL_FAILURE;
|
|
SocketContext * sock = (SocketContext *)client->net->context;
|
|
|
|
/* Use highest available and allow downgrade. If wolfSSL is built with
|
|
* old TLS support, it is possible for a server to force a downgrade to
|
|
* an insecure version. */
|
|
client->tls.ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
|
|
if (client->tls.ctx) {
|
|
wolfSSL_CTX_set_verify(client->tls.ctx, WOLFSSL_VERIFY_PEER,
|
|
mqtt_tls_verify_cb);
|
|
|
|
/* default to success */
|
|
rc = WOLFSSL_SUCCESS;
|
|
|
|
#if !defined(NO_CERT)
|
|
#if !defined(NO_FILESYSTEM)
|
|
if (sock->mqttCtx->ca_file) {
|
|
/* Load CA certificate file */
|
|
rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx,
|
|
sock->mqttCtx->ca_file, NULL);
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file,
|
|
rc, wolfSSL_ERR_reason_error_string(rc));
|
|
return rc;
|
|
}
|
|
}
|
|
if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) {
|
|
/* Load If using a mutual authentication */
|
|
rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx,
|
|
sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM);
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("Error loading certificate %s: %d (%s)",
|
|
sock->mqttCtx->mtls_certfile,
|
|
rc, wolfSSL_ERR_reason_error_string(rc));
|
|
return rc;
|
|
}
|
|
|
|
#ifdef WOLFSSL_ENCRYPTED_KEYS
|
|
/* Setup password callback for pkcs8 key */
|
|
wolfSSL_CTX_set_default_passwd_cb(client->tls.ctx,
|
|
mqtt_password_cb);
|
|
#endif
|
|
|
|
rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx,
|
|
sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM);
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("Error loading key %s: %d (%s)",
|
|
sock->mqttCtx->mtls_keyfile,
|
|
rc, wolfSSL_ERR_reason_error_string(rc));
|
|
return rc;
|
|
}
|
|
}
|
|
#else
|
|
/* Note: Zephyr example uses NO_FILESYSTEM */
|
|
#ifdef WOLFSSL_ENCRYPTED_KEYS
|
|
/* Setup password callback for pkcs8 key */
|
|
wolfSSL_CTX_set_default_passwd_cb(client->tls.ctx,
|
|
mqtt_password_cb);
|
|
#endif
|
|
/* Examples for loading buffer directly */
|
|
/* Load CA certificate buffer */
|
|
rc = wolfSSL_CTX_load_verify_buffer_ex(client->tls.ctx,
|
|
(const byte*)root_ca, (long)sizeof(root_ca),
|
|
WOLFSSL_FILETYPE_ASN1, 0, WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY);
|
|
|
|
/* Load Client Cert */
|
|
if (rc == WOLFSSL_SUCCESS) {
|
|
rc = wolfSSL_CTX_use_certificate_buffer(client->tls.ctx,
|
|
(const byte*)device_cert, (long)sizeof(device_cert),
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
}
|
|
|
|
/* Load Private Key */
|
|
if (rc == WOLFSSL_SUCCESS) {
|
|
rc = wolfSSL_CTX_use_PrivateKey_buffer(client->tls.ctx,
|
|
(const byte*)device_priv_key, (long)sizeof(device_priv_key),
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
}
|
|
#endif /* !NO_FILESYSTEM */
|
|
#endif /* !NO_CERT */
|
|
#ifdef HAVE_SNI
|
|
if ((rc == WOLFSSL_SUCCESS) && (mTlsSniHostName != NULL)) {
|
|
rc = wolfSSL_CTX_UseSNI(client->tls.ctx, WOLFSSL_SNI_HOST_NAME,
|
|
mTlsSniHostName, (word16) XSTRLEN(mTlsSniHostName));
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("UseSNI failed");
|
|
}
|
|
}
|
|
#endif /* HAVE_SNI */
|
|
#ifdef HAVE_PQC
|
|
if ((rc == WOLFSSL_SUCCESS) && (mTlsPQAlg != NULL)) {
|
|
int group = 0;
|
|
if (XSTRCMP(mTlsPQAlg, "ML_KEM_768") == 0) {
|
|
group = WOLFSSL_ML_KEM_768;
|
|
} else if (XSTRCMP(mTlsPQAlg, "SecP384r1MLKEM768") == 0) {
|
|
group = WOLFSSL_SECP384R1MLKEM768;
|
|
} else {
|
|
PRINTF("Invalid post-quantum KEM specified");
|
|
}
|
|
|
|
if (group != 0) {
|
|
client->tls.ssl = wolfSSL_new(client->tls.ctx);
|
|
if (client->tls.ssl == NULL) {
|
|
rc = WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (rc == WOLFSSL_SUCCESS) {
|
|
rc = wolfSSL_UseKeyShare(client->tls.ssl, group);
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("Use key share failed");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_PQC */
|
|
}
|
|
|
|
#if defined(NO_CERT) || defined(NO_FILESYSTEM)
|
|
(void)sock;
|
|
#endif
|
|
|
|
PRINTF("MQTT TLS Setup (%d)", rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#ifdef WOLFMQTT_SN
|
|
int mqtt_dtls_cb(MqttClient* client) {
|
|
#ifdef WOLFSSL_DTLS
|
|
int rc = WOLFSSL_FAILURE;
|
|
SocketContext * sock = (SocketContext *)client->net->context;
|
|
|
|
client->tls.ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method());
|
|
if (client->tls.ctx) {
|
|
wolfSSL_CTX_set_verify(client->tls.ctx, WOLFSSL_VERIFY_PEER,
|
|
mqtt_tls_verify_cb);
|
|
|
|
/* default to success */
|
|
rc = WOLFSSL_SUCCESS;
|
|
|
|
#if !defined(NO_CERT) && !defined(NO_FILESYSTEM)
|
|
if (sock->mqttCtx->ca_file) {
|
|
/* Load CA certificate file */
|
|
rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx,
|
|
sock->mqttCtx->ca_file, NULL);
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file,
|
|
rc, wolfSSL_ERR_reason_error_string(rc));
|
|
return rc;
|
|
}
|
|
}
|
|
if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) {
|
|
/* Load If using a mutual authentication */
|
|
rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx,
|
|
sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM);
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("Error loading certificate %s: %d (%s)",
|
|
sock->mqttCtx->mtls_certfile,
|
|
rc, wolfSSL_ERR_reason_error_string(rc));
|
|
return rc;
|
|
}
|
|
|
|
rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx,
|
|
sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM);
|
|
if (rc != WOLFSSL_SUCCESS) {
|
|
PRINTF("Error loading key %s: %d (%s)",
|
|
sock->mqttCtx->mtls_keyfile,
|
|
rc, wolfSSL_ERR_reason_error_string(rc));
|
|
return rc;
|
|
}
|
|
}
|
|
#else
|
|
(void)sock;
|
|
#endif
|
|
|
|
client->tls.ssl = wolfSSL_new(client->tls.ctx);
|
|
if (client->tls.ssl == NULL) {
|
|
rc = WOLFSSL_FAILURE;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
PRINTF("MQTT DTLS Setup (%d)", rc);
|
|
#else /* WOLFSSL_DTLS */
|
|
(void)client;
|
|
int rc = 0;
|
|
PRINTF("MQTT DTLS Setup - Must enable DTLS in wolfSSL!");
|
|
#endif
|
|
return rc;
|
|
}
|
|
#endif /* WOLFMQTT_SN */
|
|
#else
|
|
int mqtt_tls_cb(MqttClient* client)
|
|
{
|
|
(void)client;
|
|
return 0;
|
|
}
|
|
#ifdef WOLFMQTT_SN
|
|
int mqtt_dtls_cb(MqttClient* client)
|
|
{
|
|
(void)client;
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif /* ENABLE_MQTT_TLS */
|
|
|
|
int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen)
|
|
{
|
|
#if !defined(NO_FILESYSTEM)
|
|
int rc = 0;
|
|
XFILE file = NULL;
|
|
long int pos = -1L;
|
|
|
|
/* Check arguments */
|
|
if (filePath == NULL || XSTRLEN(filePath) == 0 || fileLen == NULL ||
|
|
fileBuf == NULL) {
|
|
return MQTT_CODE_ERROR_BAD_ARG;
|
|
}
|
|
|
|
/* Open file */
|
|
file = XFOPEN(filePath, "rb");
|
|
if (file == NULL) {
|
|
PRINTF("File '%s' does not exist!", filePath);
|
|
rc = EXIT_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
/* Determine length of file */
|
|
if (XFSEEK(file, 0, XSEEK_END) != 0) {
|
|
PRINTF("fseek() failed");
|
|
rc = EXIT_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
pos = (int)XFTELL(file);
|
|
if (pos == -1L) {
|
|
PRINTF("ftell() failed");
|
|
rc = EXIT_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
*fileLen = (int)pos;
|
|
if (XFSEEK(file, 0, XSEEK_SET) != 0) {
|
|
PRINTF("fseek() failed");
|
|
rc = EXIT_FAILURE;
|
|
goto exit;
|
|
}
|
|
#ifdef DEBUG_WOLFMQTT
|
|
PRINTF("File %s is %d bytes", filePath, *fileLen);
|
|
#endif
|
|
|
|
/* Allocate buffer for image */
|
|
*fileBuf = (byte*)WOLFMQTT_MALLOC(*fileLen);
|
|
if (*fileBuf == NULL) {
|
|
PRINTF("File buffer malloc failed!");
|
|
rc = MQTT_CODE_ERROR_MEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
/* Load file into buffer */
|
|
rc = (int)XFREAD(*fileBuf, 1, *fileLen, file);
|
|
if (rc != *fileLen) {
|
|
PRINTF("Error reading file! %d", rc);
|
|
rc = EXIT_FAILURE;
|
|
goto exit;
|
|
}
|
|
rc = 0; /* Success */
|
|
|
|
exit:
|
|
if (file) {
|
|
XFCLOSE(file);
|
|
}
|
|
if (rc != 0) {
|
|
if (*fileBuf) {
|
|
WOLFMQTT_FREE(*fileBuf);
|
|
*fileBuf = NULL;
|
|
}
|
|
}
|
|
return rc;
|
|
|
|
#else
|
|
(void)filePath;
|
|
(void)fileBuf;
|
|
(void)fileLen;
|
|
PRINTF("File system support is not configured.");
|
|
return EXIT_FAILURE;
|
|
#endif
|
|
}
|