diff --git a/test/broker/09-plugin-auth-acl-sub.conf b/test/broker/09-plugin-auth-acl-sub.conf new file mode 100644 index 00000000..fca62763 --- /dev/null +++ b/test/broker/09-plugin-auth-acl-sub.conf @@ -0,0 +1,3 @@ +port 1888 +allow_anonymous false +auth_plugin c/auth_plugin.so diff --git a/test/broker/09-plugin-auth-acl-sub.py b/test/broker/09-plugin-auth-acl-sub.py new file mode 100755 index 00000000..631a68a7 --- /dev/null +++ b/test/broker/09-plugin-auth-acl-sub.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +# Test topic subscription. All topic are allowed but not using wildcard in subscribe. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="readonly") +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 53 +subscribe_packet = mosq_test.gen_subscribe(mid, "qos0/test", 0) +suback_packet = mosq_test.gen_suback(mid, 0) + +mid_fail = 54 +subscribe_packet_fail = mosq_test.gen_subscribe(mid_fail, "#", 0) +suback_packet_fail = mosq_test.gen_suback(mid_fail, 0x80) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20) + sock.send(subscribe_packet) + + if mosq_test.expect_packet(sock, "suback", suback_packet): + sock.send(subscribe_packet_fail) + if mosq_test.expect_packet(sock, "suback", suback_packet_fail): + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + + +exit(rc) + diff --git a/test/broker/09-plugin-auth-defer-unpwd-fail.conf b/test/broker/09-plugin-auth-defer-unpwd-fail.conf new file mode 100644 index 00000000..fca62763 --- /dev/null +++ b/test/broker/09-plugin-auth-defer-unpwd-fail.conf @@ -0,0 +1,3 @@ +port 1888 +allow_anonymous false +auth_plugin c/auth_plugin.so diff --git a/test/broker/09-plugin-auth-defer-unpwd-fail.py b/test/broker/09-plugin-auth-defer-unpwd-fail.py new file mode 100755 index 00000000..367b07b3 --- /dev/null +++ b/test/broker/09-plugin-auth-defer-unpwd-fail.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# Test whether a connection fail when using a auth_plugin that defer authentication. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username@v2", password="doesNotMatter") +connack_packet = mosq_test.gen_connack(rc=5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20) + rc = 0 + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + + +exit(rc) + diff --git a/test/broker/09-plugin-auth-defer-unpwd-success.conf b/test/broker/09-plugin-auth-defer-unpwd-success.conf new file mode 100644 index 00000000..55f98121 --- /dev/null +++ b/test/broker/09-plugin-auth-defer-unpwd-success.conf @@ -0,0 +1,4 @@ +port 1888 +allow_anonymous false +auth_plugin c/auth_plugin.so +auth_plugin c/auth_plugin_v2.so diff --git a/test/broker/09-plugin-auth-defer-unpwd-success.py b/test/broker/09-plugin-auth-defer-unpwd-success.py new file mode 100755 index 00000000..a99d27dd --- /dev/null +++ b/test/broker/09-plugin-auth-defer-unpwd-success.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Test whether a connection is successful with correct username and password +# when using a two auth_plugin (first will defer, second will accept). + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username@v2", password="doesNotMatter") +connack_packet = mosq_test.gen_connack(rc=0) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20) + rc = 0 + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + + +exit(rc) + diff --git a/test/broker/09-plugin-auth-v2-unpwd-fail.conf b/test/broker/09-plugin-auth-v2-unpwd-fail.conf new file mode 100644 index 00000000..98bb0b51 --- /dev/null +++ b/test/broker/09-plugin-auth-v2-unpwd-fail.conf @@ -0,0 +1,3 @@ +port 1888 +allow_anonymous false +auth_plugin c/auth_plugin_v2.so diff --git a/test/broker/09-plugin-auth-v2-unpwd-fail.py b/test/broker/09-plugin-auth-v2-unpwd-fail.py new file mode 100755 index 00000000..b1d7b17d --- /dev/null +++ b/test/broker/09-plugin-auth-v2-unpwd-fail.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# Test whether a connection is successful with correct username and password +# when using a simple auth_plugin. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username", password="wrong") +connack_packet = mosq_test.gen_connack(rc=5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20) + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + +exit(rc) diff --git a/test/broker/09-plugin-auth-v2-unpwd-success.conf b/test/broker/09-plugin-auth-v2-unpwd-success.conf new file mode 100644 index 00000000..98bb0b51 --- /dev/null +++ b/test/broker/09-plugin-auth-v2-unpwd-success.conf @@ -0,0 +1,3 @@ +port 1888 +allow_anonymous false +auth_plugin c/auth_plugin_v2.so diff --git a/test/broker/09-plugin-auth-v2-unpwd-success.py b/test/broker/09-plugin-auth-v2-unpwd-success.py new file mode 100755 index 00000000..d87f1fe8 --- /dev/null +++ b/test/broker/09-plugin-auth-v2-unpwd-success.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# Test whether a connection is successful with correct username and password +# when using a simple auth_plugin. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username", password="cnwTICONIURW") +connack_packet = mosq_test.gen_connack(rc=0) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20) + rc = 0 + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + + +exit(rc) diff --git a/test/broker/Makefile b/test/broker/Makefile index f23c174c..35228406 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -100,6 +100,11 @@ endif 09 : ./09-plugin-auth-unpwd-success.py ./09-plugin-auth-unpwd-fail.py + ./09-plugin-auth-acl-sub.py + ./09-plugin-auth-v2-unpwd-success.py + ./09-plugin-auth-v2-unpwd-fail.py + ./09-plugin-auth-defer-unpwd-success.py + ./09-plugin-auth-defer-unpwd-fail.py 10 : ./10-listener-mount-point.py diff --git a/test/broker/c/Makefile b/test/broker/c/Makefile index 4e43637c..bbbfc1ae 100644 --- a/test/broker/c/Makefile +++ b/test/broker/c/Makefile @@ -2,13 +2,16 @@ CFLAGS=-I../../../lib -I../../../src -Wall -Werror -all : auth_plugin.so 08 +all : auth_plugin.so auth_plugin_v2.so 08 08 : 08-tls-psk-pub.test 08-tls-psk-bridge.test auth_plugin.so : auth_plugin.c $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ +auth_plugin_v2.so : auth_plugin_v2.c + $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ + 08-tls-psk-pub.test : 08-tls-psk-pub.c $(CC) ${CFLAGS} $^ -o $@ ../../../lib/libmosquitto.so.1 diff --git a/test/broker/c/auth_plugin.c b/test/broker/c/auth_plugin.c index d0f645f5..65728cbf 100644 --- a/test/broker/c/auth_plugin.c +++ b/test/broker/c/auth_plugin.c @@ -36,6 +36,8 @@ int mosquitto_auth_acl_check(void *user_data, int access, const struct mosquitto printf("%s\n", username); if(username && !strcmp(username, "readonly") && access == MOSQ_ACL_READ){ return MOSQ_ERR_SUCCESS; + }else if(username && !strcmp(username, "readonly") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')) { + return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } @@ -47,6 +49,8 @@ int mosquitto_auth_unpwd_check(void *user_data, const struct mosquitto *client, return MOSQ_ERR_SUCCESS; }else if(!strcmp(username, "readonly")){ return MOSQ_ERR_SUCCESS; + }else if(!strcmp(username, "test-username@v2")){ + return MOSQ_ERR_PLUGIN_DEFER; }else{ return MOSQ_ERR_AUTH; } diff --git a/test/broker/c/auth_plugin_v2.c b/test/broker/c/auth_plugin_v2.c new file mode 100644 index 00000000..3b039ef5 --- /dev/null +++ b/test/broker/c/auth_plugin_v2.c @@ -0,0 +1,67 @@ +#include +#include +#include "mosquitto_plugin_v2.h" + +/* + * Following constant come from mosquitto.h + * + * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2 + */ +enum mosq_err_t { + MOSQ_ERR_SUCCESS = 0, + MOSQ_ERR_AUTH = 11, + MOSQ_ERR_ACL_DENIED = 12 +}; + +int mosquitto_auth_plugin_version(void) +{ + return MOSQ_AUTH_PLUGIN_VERSION; +} + +int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access) +{ + if(!strcmp(username, "readonly") && access == MOSQ_ACL_READ){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_ACL_DENIED; + } +} + +int mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password) +{ + if(!strcmp(username, "test-username") && password && !strcmp(password, "cnwTICONIURW")){ + return MOSQ_ERR_SUCCESS; + }else if(!strcmp(username, "readonly")){ + return MOSQ_ERR_SUCCESS; + }else if(!strcmp(username, "test-username@v2")){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_AUTH; + } +} + +int mosquitto_auth_psk_key_get(void *user_data, const char *hint, const char *identity, char *key, int max_key_len) +{ + return MOSQ_ERR_AUTH; +} + diff --git a/test/broker/c/auth_plugin_v2.so b/test/broker/c/auth_plugin_v2.so new file mode 100755 index 00000000..3b7d95e5 Binary files /dev/null and b/test/broker/c/auth_plugin_v2.so differ diff --git a/test/broker/c/mosquitto_plugin_v2.h b/test/broker/c/mosquitto_plugin_v2.h new file mode 100644 index 00000000..5b6449c0 --- /dev/null +++ b/test/broker/c/mosquitto_plugin_v2.h @@ -0,0 +1,228 @@ +/* +Copyright (c) 2012-2014 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifndef MOSQUITTO_PLUGIN_H +#define MOSQUITTO_PLUGIN_H + +#define MOSQ_AUTH_PLUGIN_VERSION 2 + +#define MOSQ_ACL_NONE 0x00 +#define MOSQ_ACL_READ 0x01 +#define MOSQ_ACL_WRITE 0x02 + +struct mosquitto_auth_opt { + char *key; + char *value; +}; + +/* + * To create an authentication plugin you must include this file then implement + * the functions listed below. The resulting code should then be compiled as a + * shared library. Using gcc this can be achieved as follows: + * + * gcc -I -fPIC -shared plugin.c -o plugin.so + * + * On Mac OS X: + * + * gcc -I -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so + * + */ + +/* ========================================================================= + * + * Utility Functions + * + * Use these functions from within your plugin. + * + * There are also very useful functions in libmosquitto. + * + * ========================================================================= */ + +/* + * Function: mosquitto_log_printf + * + * Write a log message using the broker configured logging. + * + * Parameters: + * level - Log message priority. Can currently be one of: + * + * MOSQ_LOG_INFO + * MOSQ_LOG_NOTICE + * MOSQ_LOG_WARNING + * MOSQ_LOG_ERR + * MOSQ_LOG_DEBUG + * MOSQ_LOG_SUBSCRIBE (not recommended for use by plugins) + * MOSQ_LOG_UNSUBSCRIBE (not recommended for use by plugins) + * + * These values are defined in mosquitto.h. + * + * fmt, ... - printf style format and arguments. + */ +void mosquitto_log_printf(int level, const char *fmt, ...); + + + +/* ========================================================================= + * + * Plugin Functions + * + * You must implement these functions in your plugin. + * + * ========================================================================= */ + +/* + * Function: mosquitto_auth_plugin_version + * + * The broker will call this function immediately after loading the plugin to + * check it is a supported plugin version. Your code must simply return + * MOSQ_AUTH_PLUGIN_VERSION. + */ +int mosquitto_auth_plugin_version(void); + +/* + * Function: mosquitto_auth_plugin_init + * + * Called after the plugin has been loaded and + * has been called. This will only ever be called once and can be used to + * initialise the plugin. + * + * Parameters: + * + * user_data : The pointer set here will be passed to the other plugin + * functions. Use to hold connection information for example. + * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which + * provides the plugin options defined in the configuration file. + * auth_opt_count : The number of elements in the auth_opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count); + +/* + * Function: mosquitto_auth_plugin_cleanup + * + * Called when the broker is shutting down. This will only ever be called once. + * Note that will be called directly before + * this function. + * + * Parameters: + * + * user_data : The pointer provided in . + * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which + * provides the plugin options defined in the configuration file. + * auth_opt_count : The number of elements in the auth_opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count); + +/* + * Function: mosquitto_auth_security_init + * + * Called when the broker initialises the security functions when it starts up. + * If the broker is requested to reload its configuration whilst running, + * will be called, followed by this function. + * In this situation, the reload parameter will be true. + * + * Parameters: + * + * user_data : The pointer provided in . + * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which + * provides the plugin options defined in the configuration file. + * auth_opt_count : The number of elements in the auth_opts array. + * reload : If set to false, this is the first time the function has + * been called. If true, the broker has received a signal + * asking to reload its configuration. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload); + +/* + * Function: mosquitto_auth_security_cleanup + * + * Called when the broker cleans up the security functions when it shuts down. + * If the broker is requested to reload its configuration whilst running, + * this function will be called, followed by . + * In this situation, the reload parameter will be true. + * + * Parameters: + * + * user_data : The pointer provided in . + * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which + * provides the plugin options defined in the configuration file. + * auth_opt_count : The number of elements in the auth_opts array. + * reload : If set to false, this is the first time the function has + * been called. If true, the broker has received a signal + * asking to reload its configuration. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload); + +/* + * Function: mosquitto_auth_acl_check + * + * Called by the broker when topic access must be checked. access will be one + * of MOSQ_ACL_READ (for subscriptions) or MOSQ_ACL_WRITE (for publish). Return + * MOSQ_ERR_SUCCESS if access was granted, MOSQ_ERR_ACL_DENIED if access was + * not granted, or MOSQ_ERR_UNKNOWN for an application specific error. + */ +int mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access); + +/* + * Function: mosquitto_auth_unpwd_check + * + * Called by the broker when a username/password must be checked. Return + * MOSQ_ERR_SUCCESS if the user is authenticated, MOSQ_ERR_AUTH if + * authentication failed, or MOSQ_ERR_UNKNOWN for an application specific + * error. + */ +int mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password); + +/* + * Function: mosquitto_psk_key_get + * + * Called by the broker when a client connects to a listener using TLS/PSK. + * This is used to retrieve the pre-shared-key associated with a client + * identity. + * + * Examine hint and identity to determine the required PSK (which must be a + * hexadecimal string with no leading "0x") and copy this string into key. + * + * Parameters: + * user_data : the pointer provided in . + * hint : the psk_hint for the listener the client is connecting to. + * identity : the identity string provided by the client + * key : a string where the hex PSK should be copied + * max_key_len : the size of key + * + * Return value: + * Return 0 on success. + * Return >0 on failure. + * Return >0 if this function is not required. + */ +int mosquitto_auth_psk_key_get(void *user_data, const char *hint, const char *identity, char *key, int max_key_len); + +#endif