/*
* Copyright (c) 2013, 2014 Jan-Piet Mens <jp@mens.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of mosquitto nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*///#ifdef _WIN32//#ifdef AUTH_PLUG_EXPORTS//# define AUTH_PLUG_API __declspec(dllexport)//#else//# define AUTH_PLUG_API __declspec(dllimport)//#endif //AUTH_PLUG_EXPORT//#else//#define AUTH_PLUG_API//#endif //_WIN32#include<stdio.h>#include<string.h>#include<stdlib.h>#include<openssl/evp.h>#include<mosquitto.h>#include<mosquitto_broker.h>#include<mosquitto_plugin.h>#ifndef_WIN32#include<fnmatch.h>#endif// !_WIN32#include<time.h>#ifLIBMOSQUITTO_VERSION_NUMBER >=1004090#defineMOSQ_DENY_AUTHMOSQ_ERR_PLUGIN_DEFER#defineMOSQ_DENY_ACLMOSQ_ERR_PLUGIN_DEFER#else#defineMOSQ_DENY_AUTHMOSQ_ERR_AUTH#defineMOSQ_DENY_ACLMOSQ_ERR_ACL_DENIED#endif#ifMOSQ_AUTH_PLUGIN_VERSION >=3#definemosquitto_auth_optmosquitto_opt#endif#include"log.h"#include"hash.h"#include"backends.h"#include"envs.h"#include"be-psk.h"#include"be-cdb.h"#include"be-mysql.h"#include"be-sqlite.h"#include"be-redis.h"#include"be-memcached.h"#include"be-postgres.h"#include"be-ldap.h"#include"be-http.h"#include"be-jwt.h"#include"be-mongo.h"#include"be-files.h"#include"userdata.h"#include"cache.h"#ifdef_WIN32#include"strsep_def.h"#endif#defineSTRINGIFY(x) #x#defineTOSTRING(x)STRINGIFY(x)#defineNBACKENDS(5)#ifBE_PSK#definePSKSETUPdo{
\if(!strcmp(psk_database, q)){
\(*pskbep)->conf =(*bep)->conf;\(*pskbep)->superuser =(*bep)->superuser;\(*pskbep)->aclcheck =(*bep)->aclcheck;\}\}while(0)#else#definePSKSETUP#endifstructbackend_p{
void*conf;/* Handle to backend */char*name;
f_kill *kill;
f_getuser *getuser;
f_superuser *superuser;
f_aclcheck *aclcheck;};intpbkdf2_check(char*password,char*hash);intmosquitto_auth_plugin_version(void){
log_init();_log(LOG_NOTICE,"*** auth-plug: startup");return MOSQ_AUTH_PLUGIN_VERSION;}intmosquitto_auth_plugin_init(void**userdata,structmosquitto_auth_opt*auth_opts,int auth_opt_count){
int i;char*backends =NULL,*p,*_p,*q;structmosquitto_auth_opt*o;structuserdata*ud;int ret = MOSQ_ERR_SUCCESS;int nord;structbackend_p**bep;#ifdefBE_PSKstructbackend_p**pskbep;char*psk_database =NULL;#endiflog_init();OpenSSL_add_all_algorithms();*userdata =(structuserdata*)malloc(sizeof(structuserdata));if(*userdata ==NULL){
perror("allocting userdata");return MOSQ_ERR_UNKNOWN;}memset(*userdata,0,sizeof(structuserdata));
ud =*userdata;
ud->superusers =NULL;
ud->fallback_be =-1;
ud->anonusername =strdup("anonymous");
ud->acl_cacheseconds =300;
ud->auth_cacheseconds =0;
ud->acl_cachejitter =0;
ud->auth_cachejitter =0;
ud->aclcache =NULL;
ud->authcache =NULL;
ud->clients =NULL;/*
* Shove all options Mosquitto gives the plugin into a hash,
* and let the back-ends figure out if they have all they
* need upon init()
*/for(i =0, o = auth_opts; i < auth_opt_count; i++, o++){
// _log(LOG_DEBUG, "AuthOptions: key=%s, val=%s", o->key, o->value);p_add(o->key, o->value);if(!strcmp(o->key,"superusers"))
ud->superusers =strdup(o->value);if(!strcmp(o->key,"anonusername")){
free(ud->anonusername);
ud->anonusername =strdup(o->value);}if(!strcmp(o->key,"cacheseconds")||!strcmp(o->key,"acl_cacheseconds"))
ud->acl_cacheseconds =atol(o->value);if(!strcmp(o->key,"auth_cacheseconds"))
ud->auth_cacheseconds =atol(o->value);if(!strcmp(o->key,"acl_cachejitter"))
ud->acl_cachejitter =atol(o->value);if(!strcmp(o->key,"auth_cacheijitter"))
ud->auth_cachejitter =atol(o->value);if(!strcmp(o->key,"log_quiet")){
if(!strcmp(o->value,"false")||!strcmp(o->value,"0")){
log_quiet =0;}elseif(!strcmp(o->value,"true")||!strcmp(o->value,"1")){
log_quiet =1;}else{
_log(LOG_NOTICE,"Error: Invalid log_quiet value (%s).", o->value);}}#if0if(!strcmp(o->key,"topic_prefix"))
ud->topicprefix =strdup(o->value);#endif}/*
* Set up back-ends, and tell them to initialize themselves.
*/
backends =p_stab("backends");if(backends ==NULL){
_fatal("No backends configured.");}
_p = p =strdup(backends);_log(LOG_NOTICE,"** Configured order: %s\n", p);
ud->be_list =(structbackend_p**)malloc((sizeof(structbackend_p*))*(NBACKENDS +1));
bep = ud->be_list;
nord =0;#ifBE_PSK/*
* Force adding PSK back-end, which must be indexed at 0
* The PSK back-end is a little special in that it will use
* a database from another back-end (e.g. mysql or sqlite)
* for authorization.
*/if((psk_database =p_stab("psk_database"))==NULL){
_fatal("PSK is configured so psk_database needs to be set");}
pskbep = bep;*pskbep =(structbackend_p*)malloc(sizeof(structbackend_p));memset(*pskbep,0,sizeof(structbackend_p));(*pskbep)->name =strdup("psk");
bep = pskbep;
bep++;
nord++;#endif/* BE_PSK */for(q =strsep(&p,","); q &&*q &&(nord < NBACKENDS); q =strsep(&p,",")){
int found =0;#ifBE_MYSQLif(!strcmp(q,"mysql")){
*bep =(structbackend_p*)malloc(sizeof(structbackend_p));memset(*bep,0,sizeof(structbackend_p));(*bep)->name =strdup("mysql");(*bep)->conf =be_mysql_init();if((*bep)->conf ==NULL){
_fatal("%s init returns NULL", q);}(*bep)->kill = be_mysql_destroy;(*bep)->getuser = be_mysql_getuser;(*bep)->superuser = be_mysql_superuser;(*bep)->aclcheck = be_mysql_aclcheck;
found =1;
ud->fallback_be = ud->fallback_be ==-1? nord : ud->fallback_be;
PSKSETUP;}#endif#ifBE_POSTGRESif(!strcmp(q,"postgres")){
*bep =(structbackend_p*)malloc(sizeof(structbackend_p));memset(*bep,0,sizeof(structbackend_p));(*bep)->name =strdup("postgres");(*bep)->conf =be_pg_init();if((*bep)->conf ==NULL){
_fatal("%s init returns NULL", q);}(*bep)->kill = be_pg_destroy;(*bep)->getuser = be_pg_getuser;(*bep)->superuser = be_pg_superuser;(*bep)->aclcheck = be_pg_aclcheck;
found =1;
ud->fallback_be = ud->fallback_be ==-1? nord : ud->fallback_be;
PSKSETUP;}#endif#ifBE_LDAPif(!strcmp(q,"ldap")){
*bep =(structbackend_p*)malloc(sizeof(structbackend_p));memset(*bep,0