Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions include/mosquitto/broker.h
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,36 @@ mosq_EXPORT int mosquitto_broker_publish_copy(
*/
mosq_EXPORT void mosquitto_complete_basic_auth(const char *clientid, int result);

/* Function: mosquitto_complete_extended_auth
*
* Complete a delayed extended authentication request.
*
* Useful for plugins that subscribe to the MOSQ_EVT_EXT_AUTH_START and
* MOSQ_EVT_EXT_AUTH_CONTINUE events. If your plugin makes authentication
* requests that are not "instant", in particular if they communciate with an
* external service, then instead of blocking for a reply and returning
* MOSQ_ERR_SUCCESS, MOSQ_ERR_AUTH_CONTINUE, or MOSQ_ERR_AUTH, the plugin can
* return MOSQ_ERR_AUTH_DELAYED. This means that the plugin is promising to
* tell the broker the authentication result in the future. Once the plugin has
* an answer, it should call `mosquitto_complete_extended_auth()`, passing the
* client id, result, and any additional auth data. Note that it is possible to
* pass MOSQ_ERR_AUTH_CONTINUE to procede with additional authentication steps.
*
* Parameters:
* clientid - ID of the client being authenticated
* result - authentication result. This will typically be
* MOSQ_ERR_SUCCESS, MOSQ_ERR_AUTH_CONTINUE, or MOSQ_ERR_AUTH.
* Other error codes can be used if more appropriate, and
* the client connection will still be rejected. e.g.,
* MOSQ_ERR_NOMEM.
* auth_data_out - optional auth_data bytes. Mosquitto will free any data
* passed here, so string literals for example are not valid.
* May be NULL.
* auth_data_out_len - auth data length in bytes. May be 0 if no data is sent.
*
*/
mosq_EXPORT void mosquitto_complete_extended_auth(const char *clientid, int result, void *auth_data_out, uint16_t auth_data_out_len);


/* Function: mosquitto_broker_node_id_set
*
Expand Down
3 changes: 3 additions & 0 deletions lib/mosquitto_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ enum mosquitto_client_state {
mosq_cs_authenticating = 20, /* Client has sent CONNECT but is still undergoing extended authentication */
mosq_cs_reauthenticating = 21, /* Client is undergoing reauthentication and shouldn't do anything else until complete */
mosq_cs_delayed_auth = 22, /* Client is awaiting an authentication result from a plugin */
mosq_cs_delayed_ext_auth = 23, /* Client is awaiting an extended authentication result from a plugin */
mosq_cs_delayed_ext_reauth = 24, /* Client is awaiting a reauthentication result from a plugin */
};

enum mosquitto__protocol {
Expand Down Expand Up @@ -451,6 +453,7 @@ struct mosquitto {
#ifdef WITH_BROKER
UT_hash_handle hh_id;
UT_hash_handle hh_sock;
UT_hash_handle hh_id_delayed_auth;
struct mosquitto *for_free_next;
struct session_expiry_list *expiry_list_item;
uint16_t remote_port;
Expand Down
67 changes: 55 additions & 12 deletions plugins/examples/delayed-auth/mosquitto_delayed_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0
* This is an example plugin showing how to carry out delayed authentication.
* The "authentication" in this example makes no checks whatsoever, but delays
* the response by 5 seconds, and randomly chooses whether it should succeed.
* The example supports basic and extended authentication.
*
* Compile with:
* gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_delayed_auth.c -o mosquitto_delayed_auth.so
Expand All @@ -33,6 +34,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0


#include <limits.h>
#include <mosquitto/broker.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
Expand All @@ -54,6 +56,7 @@ struct client_list {
UT_hash_handle hh;
char *id;
time_t request_time;
bool basic_auth;
};

static mosquitto_plugin_id_t *mosq_pid = NULL;
Expand All @@ -71,16 +74,9 @@ static bool authentication_check(struct client_list *client, time_t now)
}


static int basic_auth_callback(int event, void *event_data, void *userdata)
static int start_auth(int event, const char *id)
{
struct mosquitto_evt_basic_auth *ed = event_data;
static struct client_list *client;
const char *id;

UNUSED(event);
UNUSED(userdata);

id = mosquitto_client_id(ed->client);

HASH_FIND(hh, clients, id, strlen(id), client);
if(client){
Expand All @@ -97,12 +93,47 @@ static int basic_auth_callback(int event, void *event_data, void *userdata)
return MOSQ_ERR_NOMEM;
}
client->request_time = time(NULL);
HASH_ADD_KEYPTR(hh, clients, client->id, strlen(client->id), client);
if(event == MOSQ_EVT_BASIC_AUTH){
mosquitto_log_printf(MOSQ_LOG_DEBUG, "Starting basic auth for %s at %ld", client->id, time(NULL));
client->basic_auth = true;
}else{
mosquitto_log_printf(MOSQ_LOG_DEBUG, "Starting extended auth for %s at %ld", client->id, time(NULL));
client->basic_auth = false;
}

mosquitto_log_printf(MOSQ_LOG_DEBUG, "Starting auth for %s at %ld", client->id, time(NULL));
HASH_ADD_KEYPTR(hh, clients, client->id, strlen(client->id), client);
}

return MOSQ_ERR_AUTH_DELAYED;

}


static int basic_auth_callback(int event, void *event_data, void *userdata)
{
struct mosquitto_evt_basic_auth *ed = event_data;
const char *id;

UNUSED(event);
UNUSED(userdata);

id = mosquitto_client_id(ed->client);

return start_auth(event, id);
}


static int extended_auth_callback(int event, void *event_data, void *userdata)
{
struct mosquitto_evt_extended_auth *ed = event_data;
const char *id;

UNUSED(event);
UNUSED(userdata);

id = mosquitto_client_id(ed->client);

return start_auth(event, id);
}


Expand All @@ -128,9 +159,17 @@ static int tick_callback(int event, void *event_data, void *userdata)
r = random() % 1000;
#endif
if(r > 740){
mosquitto_complete_basic_auth(client->id, MOSQ_ERR_AUTH);
if(client->basic_auth){
mosquitto_complete_basic_auth(client->id, MOSQ_ERR_AUTH);
}else{
mosquitto_complete_extended_auth(client->id, MOSQ_ERR_AUTH, NULL, 0);
}
}else{
mosquitto_complete_basic_auth(client->id, MOSQ_ERR_SUCCESS);
if(client->basic_auth){
mosquitto_complete_basic_auth(client->id, MOSQ_ERR_SUCCESS);
}else{
mosquitto_complete_extended_auth(client->id, MOSQ_ERR_SUCCESS, NULL, 0);
}
}
mosquitto_log_printf(MOSQ_LOG_DEBUG, "Completing auth for %s at %ld", client->id, now);
HASH_DELETE(hh, clients, client);
Expand Down Expand Up @@ -162,6 +201,10 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, s
if(rc){
return rc;
}
rc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_EXT_AUTH_START, extended_auth_callback, NULL, NULL);
if(rc){
return rc;
}
rc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_TICK, tick_callback, NULL, NULL);
return rc;
}
Expand Down
4 changes: 2 additions & 2 deletions src/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,9 @@ void context__remove_from_by_id(struct mosquitto *context)
struct mosquitto *context_found;

if(context->in_by_id == true && context->id){
HASH_FIND(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context_found);
HASH_FIND(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context_found);
if(context_found){
HASH_DELETE(hh_id, db.contexts_by_id_delayed_auth, context_found);
HASH_DELETE(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context_found);
}

HASH_FIND(hh_id, db.contexts_by_id, context->id, strlen(context->id), context_found);
Expand Down
10 changes: 10 additions & 0 deletions src/handle_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause

#include "config.h"

#include <mosquitto/defs.h>
#include <stdio.h>
#include <string.h>

Expand Down Expand Up @@ -125,6 +126,15 @@ int handle__auth(struct mosquitto *context)
rc = send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len);
SAFE_FREE(auth_data_out);
return rc;
}else if(rc == MOSQ_ERR_AUTH_DELAYED){
SAFE_FREE(auth_data_out);
if(context->state == mosq_cs_authenticating){
mosquitto__set_state(context, mosq_cs_delayed_ext_auth);
}else{
mosquitto__set_state(context, mosq_cs_delayed_ext_reauth);
}
HASH_ADD_KEYPTR(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context);
return MOSQ_ERR_SUCCESS;
}else{
SAFE_FREE(auth_data_out);
if(context->state == mosq_cs_authenticating && context->will){
Expand Down
8 changes: 6 additions & 2 deletions src/handle_connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,7 @@ int handle__connect(struct mosquitto *context)
}

/* Check for an existing delayed auth check, reject if present */
HASH_FIND(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), found_context);
HASH_FIND(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), found_context);
if(found_context){
rc = MOSQ_ERR_UNKNOWN;
goto handle_connect_error;
Expand All @@ -1184,6 +1184,10 @@ int handle__connect(struct mosquitto *context)
rc = send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len);
SAFE_FREE(auth_data_out);
return rc;
}else if(rc == MOSQ_ERR_AUTH_DELAYED){
mosquitto__set_state(context, mosq_cs_delayed_ext_auth);
HASH_ADD_KEYPTR(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context);
return MOSQ_ERR_SUCCESS;
}else{
SAFE_FREE(auth_data_out);
will__clear(context);
Expand Down Expand Up @@ -1214,7 +1218,7 @@ int handle__connect(struct mosquitto *context)
break;
case MOSQ_ERR_AUTH_DELAYED:
mosquitto__set_state(context, mosq_cs_delayed_auth);
HASH_ADD_KEYPTR(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context);
HASH_ADD_KEYPTR(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context);
return MOSQ_ERR_SUCCESS;
break;
case MOSQ_ERR_AUTH:
Expand Down
1 change: 1 addition & 0 deletions src/linker-aix.syms
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mosquitto_client_protocol_version
mosquitto_client_sub_count
mosquitto_client_username
mosquitto_complete_basic_auth
mosquitto_complete_extended_auth
mosquitto_control_command_reply
mosquitto_control_generic_callback
mosquitto_control_send_response
Expand Down
1 change: 1 addition & 0 deletions src/linker-macosx.syms
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ _mosquitto_client_sub_count
_mosquitto_client_username
_mosquitto_client_will_set
_mosquitto_complete_basic_auth
_mosquitto_complete_extended_auth
_mosquitto_control_command_reply
_mosquitto_control_generic_callback
_mosquitto_control_send_response
Expand Down
1 change: 1 addition & 0 deletions src/linker.syms
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
mosquitto_client_username;
mosquitto_client_will_set;
mosquitto_complete_basic_auth;
mosquitto_complete_extended_auth;
mosquitto_control_command_reply;
mosquitto_control_generic_callback;
mosquitto_control_send_response;
Expand Down
64 changes: 61 additions & 3 deletions src/plugin_public.c
Original file line number Diff line number Diff line change
Expand Up @@ -864,9 +864,9 @@ BROKER_EXPORT void mosquitto_complete_basic_auth(const char *clientid, int resul
return;
}

HASH_FIND(hh_id, db.contexts_by_id_delayed_auth, clientid, strlen(clientid), context);
if(context){
HASH_DELETE(hh_id, db.contexts_by_id_delayed_auth, context);
HASH_FIND(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, clientid, strlen(clientid), context);
if(context && context->state == mosq_cs_delayed_auth){
HASH_DELETE(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context);
if(result == MOSQ_ERR_SUCCESS){
connect__on_authorised(context, NULL, 0);
}else{
Expand All @@ -884,6 +884,64 @@ BROKER_EXPORT void mosquitto_complete_basic_auth(const char *clientid, int resul
}


BROKER_EXPORT void mosquitto_complete_extended_auth(const char *clientid, int result, void *auth_data_out, uint16_t auth_data_out_len)
{
struct mosquitto *context;

if(clientid == NULL){
return;
}

HASH_FIND(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, clientid, strlen(clientid), context);
if(context && (context->state == mosq_cs_delayed_ext_auth || context->state == mosq_cs_delayed_ext_reauth)){
HASH_DELETE(hh_id_delayed_auth, db.contexts_by_id_delayed_auth, context);
if(result == MOSQ_ERR_SUCCESS){
if(context->state == mosq_cs_delayed_ext_auth){
connect__on_authorised(context, auth_data_out, auth_data_out_len);
}else{
mosquitto__set_state(context, mosq_cs_active);
send__auth(context, MQTT_RC_SUCCESS, auth_data_out, auth_data_out_len);
SAFE_FREE(auth_data_out);
}
}else if(result == MOSQ_ERR_AUTH_CONTINUE){
if(context->state == mosq_cs_delayed_ext_auth){
mosquitto__set_state(context, mosq_cs_authenticating);
}else{
mosquitto__set_state(context, mosq_cs_reauthenticating);
}
send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len);
SAFE_FREE(auth_data_out);
}else{
SAFE_FREE(auth_data_out);
if(context->state == mosq_cs_delayed_ext_auth && context->will){
/* Free will without sending if this is our first authentication attempt */
will__clear(context);
}
if(result == MOSQ_ERR_AUTH){
if(context->state == mosq_cs_delayed_ext_auth){
send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);
mosquitto_FREE(context->id);
}else{
send__disconnect(context, MQTT_RC_NOT_AUTHORIZED, NULL);
}
}else if(result == MOSQ_ERR_NOT_SUPPORTED){
/* Client has requested extended authentication, but we don't support it. */
if(context->state == mosq_cs_delayed_ext_auth){
send__connack(context, 0, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL);
mosquitto_FREE(context->id);
}else{
send__disconnect(context, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL);
}
}else{
if(context->state == mosq_cs_delayed_ext_auth){
mosquitto_FREE(context->id);
}
}
}
}
}


BROKER_EXPORT int mosquitto_broker_node_id_set(uint16_t id)
{
if(id > 1023){
Expand Down
Loading