Skip to content

Commit

Permalink
Add mosquitto_ext_auth_continue()
Browse files Browse the repository at this point in the history
  • Loading branch information
ralight committed Oct 28, 2024
1 parent dd2c25c commit a2fe980
Show file tree
Hide file tree
Showing 25 changed files with 574 additions and 2 deletions.
3 changes: 3 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ Client library:
gives access to reason codes for each of the unsubscription requests.
- Add `mosquitto_property_remove`, for removing properties from property
lists.
- Add `on_ext_auth()` callback to allow handling MQTT v5 extended authentication.
- Add `mosquitto_ext_auth_continue()` function to continue an MQTT v5 extended
authentication.

Clients:
- Add `-W` timeout support to Windows.
Expand Down
20 changes: 20 additions & 0 deletions include/mosquitto/libmosquitto_auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ extern "C" {
*/
libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password);

/*
* Function: mosquitto_ext_auth_continue
*
* Use within an on_ext_auth callback only.
*
* Call to continue the MQTT v5 extended authentication flow.
*
* Parameters:
* mosq - a valid mosquitto instance.
* auth_method - the authentication method as provided in the on_ext_auth callback
* auth_data - authentication data to send to the broker, or NULL
* auth_data_len - the length of auth_data, in bytes, or 0
*
* Returns:
* MOSQ_ERR_SUCCESS - on success.
* MOSQ_ERR_INVAL - if the input parameters were invalid.
* MOSQ_ERR_NOMEM - if an out of memory condition occurred.
*/
libmosq_EXPORT int mosquitto_ext_auth_continue(struct mosquitto *context, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props);

#ifdef __cplusplus
}
#endif
Expand Down
32 changes: 32 additions & 0 deletions include/mosquitto/libmosquitto_callbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,38 @@ libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq
*/
libmosq_EXPORT void mosquitto_unsubscribe2_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props));

/*
* Function: mosquitto_ext_auth_callback_set
*
* Set the callback for extended authentication. This should be used if you
* want to support MQTT v5.0 extended authentication.
*
* mosq - a valid mosquitto instance.
* on_ext_auth - a callback function in the following form:
* void callback(struct mosquitto *mosq, void *obj, const char *auth_method, int auth_data_len, const void *auth_data, const mosquitto_property *props)
*
* Callback Parameters:
* mosq - the mosquitto instance making the callback.
* obj - the user data provided in <mosquitto_new>
* auth_method - the authentication method provided by the broker
* auth_data_len - the length of auth_data in bytes
* auth_data - the authentication data, or NULL
* props - list of MQTT 5 properties sent
* note that this includes the auth-method and auth-data
* properties, so you cannot use it directly with
* mosquitto_ext_auth_continue and must instead create your
* own property list
*
* Callback Return:
* MOSQ_ERR_SUCCESS - if you accept the authentication data
* MOSQ_ERR_AUTH - if the authentication should fail
* MOSQ_ERR_NOMEM - on out of memory
*
* See Also:
* <mosquitto_ext_auth_continue>
*/
void mosquitto_ext_auth_callback_set(struct mosquitto *mosq, int (*on_auth)(struct mosquitto *, void *, const char *, uint16_t, const void *, const mosquitto_property *props));

/*
* Function: mosquitto_log_callback_set
*
Expand Down
2 changes: 2 additions & 0 deletions include/mosquitto/libmosquittopp.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class mosqpp_EXPORT mosquittopp {
void message_retry_set(unsigned int message_retry);
void user_data_set(void *userdata);
int tls_set(const char *cafile, const char *capath=NULL, const char *certfile=NULL, const char *keyfile=NULL, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata)=NULL);
int ext_auth_continue(const char *auth_method, uint16_t auth_data_len=0, const void *auth_data=NULL, const mosquitto_property *properties=NULL);
int tls_opts_set(int cert_reqs, const char *tls_version=NULL, const char *ciphers=NULL);
int tls_insecure_set(bool value);
int tls_psk_set(const char *psk, const char *identity, const char *ciphers=NULL);
Expand Down Expand Up @@ -156,6 +157,7 @@ class mosqpp_EXPORT mosquittopp {
virtual void on_unsubscribe_v5(int /*mid*/, const mosquitto_property * /*props*/) {return;}
virtual void on_log(int /*level*/, const char * /*str*/) {return;}
virtual void on_error() {return;}
virtual int on_ext_auth(const char * /*auth_method*/, uint16_t /*auth_data_len*/, const void * /*auth_data*/, const mosquitto_property * /*props*/) {return MOSQ_ERR_AUTH;}
};

}
Expand Down
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(C_SRC
alias_mosq.c alias_mosq.h
callbacks.c
connect.c
extended_auth.c
handle_auth.c
handle_connack.c
handle_disconnect.c
Expand Down
1 change: 1 addition & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ OBJS= \
alias_mosq.o \
callbacks.o \
connect.o \
extended_auth.o \
handle_auth.o \
handle_connack.o \
handle_disconnect.o \
Expand Down
25 changes: 25 additions & 0 deletions lib/callbacks.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mo
}


void mosquitto_ext_auth_callback_set(struct mosquitto *mosq, int (*on_ext_auth)(struct mosquitto *, void *, const char *, uint16_t, const void *, const mosquitto_property *props))
{
pthread_mutex_lock(&mosq->callback_mutex);
mosq->on_ext_auth = on_ext_auth;
pthread_mutex_unlock(&mosq->callback_mutex);
}


void callback__on_pre_connect(struct mosquitto *mosq)
{
void (*on_pre_connect)(struct mosquitto *, void *userdata);
Expand Down Expand Up @@ -286,3 +294,20 @@ void callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_pro
}
mosq->callback_depth--;
}

int callback__on_ext_auth(struct mosquitto *mosq, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties)
{
int rc = MOSQ_ERR_AUTH;
int (*on_ext_auth)(struct mosquitto *, void *userdata, const char *, uint16_t, const void *, const mosquitto_property *props);

pthread_mutex_lock(&mosq->callback_mutex);
on_ext_auth = mosq->on_ext_auth;
pthread_mutex_unlock(&mosq->callback_mutex);

mosq->callback_depth++;
if(on_ext_auth){
rc = on_ext_auth(mosq, mosq->userdata, auth_method, auth_data_len, auth_data, properties);
}
mosq->callback_depth--;
return rc;
}
1 change: 1 addition & 0 deletions lib/callbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ void callback__on_message(struct mosquitto *mosq, const struct mosquitto_message
void callback__on_subscribe(struct mosquitto *mosq, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);
void callback__on_unsubscribe(struct mosquitto *mosq, int mid, int reason_code_count, const int *reason_codes, const mosquitto_property *props);
void callback__on_disconnect(struct mosquitto *mosq, int rc, const mosquitto_property *props);
int callback__on_ext_auth(struct mosquitto *mosq, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties);

#endif
14 changes: 14 additions & 0 deletions lib/cpp/mosquittopp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ static void on_unsubscribe_v5_wrapper(struct mosquitto *mosq, void *userdata, in
}


static int on_ext_auth_wrapper(struct mosquitto *mosq, void *userdata, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props)
{
class mosquittopp *m = (class mosquittopp *)userdata;
UNUSED(mosq);
return m->on_ext_auth(auth_method, auth_data_len, auth_data, props);
}


static void on_log_wrapper(struct mosquitto *mosq, void *userdata, int level, const char *str)
{
class mosquittopp *m = (class mosquittopp *)userdata;
Expand Down Expand Up @@ -273,6 +281,7 @@ void mosquitto_callbacks_set(struct mosquitto *mosq) {
mosquitto_subscribe_v5_callback_set(mosq, on_subscribe_v5_wrapper);
mosquitto_unsubscribe_callback_set(mosq, on_unsubscribe_wrapper);
mosquitto_unsubscribe_v5_callback_set(mosq, on_unsubscribe_v5_wrapper);
mosquitto_ext_auth_callback_set(mosq, on_ext_auth_wrapper);
mosquitto_log_callback_set(mosq, on_log_wrapper);
}
}
Expand Down Expand Up @@ -345,6 +354,11 @@ int mosquittopp::disconnect_v5(int reason_code, const mosquitto_property *proper
return mosquitto_disconnect_v5(m_mosq, reason_code, properties);
}

int mosquittopp::ext_auth_continue(const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *properties)
{
return mosquitto_ext_auth_continue(m_mosq, auth_method, auth_data_len, auth_data, properties);
}

int mosquittopp::socket()
{
return mosquitto_socket(m_mosq);
Expand Down
64 changes: 64 additions & 0 deletions lib/extended_auth.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright (c) 2019-2024 Roger Light <[email protected]>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/

#include "config.h"

#include "mosquitto/mqtt_protocol.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "util_mosq.h"

int mosquitto_ext_auth_continue(struct mosquitto *context, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *input_props)
{
struct mosquitto__packet *packet = NULL;
int rc;
uint32_t remaining_length;
mosquitto_property *properties = NULL;

rc = mosquitto_property_copy_all(&properties, input_props);
if(rc) return rc;

if(!context || context->protocol != mosq_p_mqtt5 || !auth_method) return MOSQ_ERR_PROTOCOL;

remaining_length = 1;

rc = mosquitto_property_add_string(&properties, MQTT_PROP_AUTHENTICATION_METHOD, auth_method);
if(rc) goto error;

if(auth_data != NULL && auth_data_len > 0){
rc = mosquitto_property_add_binary(&properties, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len);
if(rc) goto error;
}

remaining_length += mosquitto_property_get_remaining_length(properties);

rc = packet__check_oversize(context, remaining_length);
if(rc) goto error;

rc = packet__alloc(&packet, CMD_AUTH, remaining_length);
if(rc) goto error;

packet__write_byte(packet, MQTT_RC_CONTINUE_AUTHENTICATION);
property__write_all(packet, properties, true);
mosquitto_property_free_all(&properties);

return packet__queue(context, packet);
error:
mosquitto_property_free_all(&properties);
return rc;
}
12 changes: 10 additions & 2 deletions lib/handle_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#include <stdio.h>
#include <string.h>

#include "callbacks.h"
#include "logging_mosq.h"
#include "mosquitto_internal.h"
#include "mosquitto/mqtt_protocol.h"
Expand All @@ -33,6 +34,9 @@ int handle__auth(struct mosquitto *mosq)
{
int rc = 0;
uint8_t reason_code;
char *auth_method = NULL;
void *auth_data = NULL;
uint16_t auth_data_len = 0;
mosquitto_property *properties = NULL;

if(!mosq) return MOSQ_ERR_INVAL;
Expand All @@ -49,7 +53,11 @@ int handle__auth(struct mosquitto *mosq)

rc = property__read_all(CMD_AUTH, &mosq->in_packet, &properties);
if(rc) return rc;
mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */

return MOSQ_ERR_SUCCESS;
mosquitto_property_read_string(properties, MQTT_PROP_AUTHENTICATION_METHOD, &auth_method, false);
mosquitto_property_read_binary(properties, MQTT_PROP_AUTHENTICATION_DATA, &auth_data, &auth_data_len, false);
rc = callback__on_ext_auth(mosq, auth_method, auth_data_len, auth_data, properties);
mosquitto_property_free_all(&properties);

return rc;
}
2 changes: 2 additions & 0 deletions lib/linker.version
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ MOSQ_1.7 {

MOSQ_2.1 {
global:
mosquitto_ext_auth_callback_set;
mosquitto_ext_auth_continue;
mosquitto_pre_connect_callback_set;
mosquitto_topic_matches_sub_with_pattern;
mosquitto_sub_matches_acl;
Expand Down
1 change: 1 addition & 0 deletions lib/mosquitto_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ struct mosquitto {
void (*on_unsubscribe)(struct mosquitto *, void *userdata, int mid);
void (*on_unsubscribe_v5)(struct mosquitto *, void *userdata, int mid, const mosquitto_property *props);
void (*on_unsubscribe2_v5)(struct mosquitto *, void *userdata, int mid, int reason_code_count, const int *reason_codes, const mosquitto_property *props);
int (*on_ext_auth)(struct mosquitto *, void *userdata, const char *auth_method, uint16_t auth_data_len, const void *auth_data, const mosquitto_property *props);
void (*on_log)(struct mosquitto *, void *userdata, int level, const char *str);
/*void (*on_error)();*/
char *host;
Expand Down
28 changes: 28 additions & 0 deletions test/lib/01-extended-auth-continue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3

from mosq_test_helper import *
import mqtt5_rc

def do_test(conn, data):
props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 1000)
props += mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20)
connect_packet = mosq_test.gen_connect("01-extended-auth", proto_ver=5, properties=props)

props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "test-method")
props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "test-request") # This is really a binary property
auth_continue_b2c = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props)

props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "test-method")
props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "test-reply") # This is really a binary property
auth_continue_c2b = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props)
connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)

disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)

mosq_test.do_receive_send(conn, connect_packet, auth_continue_b2c, "auth_b2c")
mosq_test.do_receive_send(conn, auth_continue_c2b, connack_packet, "connack")
mosq_test.expect_packet(conn, "disconnect", disconnect_packet)


mosq_test.client_test("c/01-extended-auth-continue.test", [], do_test, None)
mosq_test.client_test("cpp/01-extended-auth-continue.test", [], do_test, None)
24 changes: 24 additions & 0 deletions test/lib/01-extended-auth-failure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env python3

from mosq_test_helper import *
import mqtt5_rc

def do_test(conn, data):
props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 1000)
props += mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20)
connect_packet = mosq_test.gen_connect("01-extended-auth", proto_ver=5, properties=props)

props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "test-method")
props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "test-request") # This is really a binary property
auth_continue_b2c = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props)

disconnect_packet = mosq_test.gen_disconnect(proto_ver=5)

mosq_test.do_receive_send(conn, connect_packet, auth_continue_b2c, "auth_b2c")
p = conn.recv(1)
if len(p) == 1:
exit(1)


mosq_test.client_test("c/01-extended-auth-failure.test", [], do_test, None)
mosq_test.client_test("cpp/01-extended-auth-failure.test", [], do_test, None)
2 changes: 2 additions & 0 deletions test/lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ test : test-compile
./01-con-discon-will.py
./01-con-discon-will-v5.py
./01-con-discon-will-clear.py
./01-extended-auth-continue.py
./01-extended-auth-failure.py
./01-keepalive-pingreq.py
./01-no-clean-session.py
./01-server-keepalive-pingreq.py
Expand Down
Loading

0 comments on commit a2fe980

Please sign in to comment.