add mysql acl, auth plugin
1. add pbkdf2 secure password hasher 2. acl table should like |username|topic|rw| in mysql where rw=1 means read, rw=2 means write, where username = '*' means any user's username in this change 3. note plugin.config no work here, but this is a working diff when default value is right
This commit is contained in:
parent
35a29994fb
commit
754a2409e2
|
@ -0,0 +1,32 @@
|
||||||
|
REBAR?=./rebar
|
||||||
|
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(REBAR) clean
|
||||||
|
rm -rf logs
|
||||||
|
rm -rf .eunit
|
||||||
|
rm -f test/*.beam
|
||||||
|
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
git clean -fxd
|
||||||
|
|
||||||
|
build: depends
|
||||||
|
$(REBAR) compile
|
||||||
|
|
||||||
|
|
||||||
|
eunit:
|
||||||
|
$(REBAR) eunit skip_deps=true
|
||||||
|
|
||||||
|
|
||||||
|
check: build eunit
|
||||||
|
|
||||||
|
|
||||||
|
%.beam: %.erl
|
||||||
|
erlc -o test/ $<
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: all clean distclean depends build eunit check
|
|
@ -0,0 +1,49 @@
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Authentication with user table of MySQL database.
|
||||||
|
|
||||||
|
## etc/plugin.config
|
||||||
|
|
||||||
|
```erlang
|
||||||
|
[
|
||||||
|
{emysql, [
|
||||||
|
{pool, 4},
|
||||||
|
{host, "localhost"},
|
||||||
|
{port, 3306},
|
||||||
|
{username, ""},
|
||||||
|
{password, ""},
|
||||||
|
{database, "mqtt"},
|
||||||
|
{encoding, utf8}
|
||||||
|
]},
|
||||||
|
{emqttd_auth_mysql, [
|
||||||
|
{user_table, mqtt_users},
|
||||||
|
%% plain password only
|
||||||
|
{password_hash, plain},
|
||||||
|
{field_mapper, [
|
||||||
|
{username, username},
|
||||||
|
{password, password}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
].
|
||||||
|
```
|
||||||
|
|
||||||
|
## Users Table(Demo)
|
||||||
|
|
||||||
|
Notice: This is a demo table. You could authenticate with any user tables.
|
||||||
|
|
||||||
|
```
|
||||||
|
CREATE TABLE `mqtt_users` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`username` varchar(60) DEFAULT NULL,
|
||||||
|
`password` varchar(60) DEFAULT NULL,
|
||||||
|
`salt` varchar(20) DEFAULT NULL,
|
||||||
|
`created` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `mqtt_users_username` (`username`)
|
||||||
|
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Load Plugin
|
||||||
|
|
||||||
|
Merge the'etc/plugin.config' to emqttd/etc/plugins.config, and the plugin will be loaded by the broker.
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Hgskolan
|
||||||
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||||
|
* 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. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the Kungliga Tekniska
|
||||||
|
* Hgskolan and its contributors.
|
||||||
|
*
|
||||||
|
* 4. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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 HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
/*RCSID("$Id: base64.c,v 1.1 2005/02/11 07:34:35 jpm Exp jpm $");*/
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
static int pos(char c)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
for(p = base64; *p; p++)
|
||||||
|
if(*p == c)
|
||||||
|
return p - base64;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_encode(const void *data, int size, char **str)
|
||||||
|
{
|
||||||
|
char *s, *p;
|
||||||
|
int i;
|
||||||
|
int c;
|
||||||
|
unsigned char *q;
|
||||||
|
|
||||||
|
p = s = (char*)malloc(size*4/3+4);
|
||||||
|
if (p == NULL)
|
||||||
|
return -1;
|
||||||
|
q = (unsigned char*)data;
|
||||||
|
i=0;
|
||||||
|
for(i = 0; i < size;){
|
||||||
|
c=q[i++];
|
||||||
|
c*=256;
|
||||||
|
if(i < size)
|
||||||
|
c+=q[i];
|
||||||
|
i++;
|
||||||
|
c*=256;
|
||||||
|
if(i < size)
|
||||||
|
c+=q[i];
|
||||||
|
i++;
|
||||||
|
p[0]=base64[(c&0x00fc0000) >> 18];
|
||||||
|
p[1]=base64[(c&0x0003f000) >> 12];
|
||||||
|
p[2]=base64[(c&0x00000fc0) >> 6];
|
||||||
|
p[3]=base64[(c&0x0000003f) >> 0];
|
||||||
|
if(i > size)
|
||||||
|
p[3]='=';
|
||||||
|
if(i > size+1)
|
||||||
|
p[2]='=';
|
||||||
|
p+=4;
|
||||||
|
}
|
||||||
|
*p=0;
|
||||||
|
*str = s;
|
||||||
|
return strlen(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_decode(const char *str, void *data)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
unsigned char *q;
|
||||||
|
int c;
|
||||||
|
int x;
|
||||||
|
int done = 0;
|
||||||
|
q=(unsigned char*)data;
|
||||||
|
for(p=str; *p && !done; p+=4){
|
||||||
|
x = pos(p[0]);
|
||||||
|
if(x >= 0)
|
||||||
|
c = x;
|
||||||
|
else{
|
||||||
|
done = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c*=64;
|
||||||
|
|
||||||
|
x = pos(p[1]);
|
||||||
|
if(x >= 0)
|
||||||
|
c += x;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
c*=64;
|
||||||
|
|
||||||
|
if(p[2] == '=')
|
||||||
|
done++;
|
||||||
|
else{
|
||||||
|
x = pos(p[2]);
|
||||||
|
if(x >= 0)
|
||||||
|
c += x;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
c*=64;
|
||||||
|
|
||||||
|
if(p[3] == '=')
|
||||||
|
done++;
|
||||||
|
else{
|
||||||
|
if(done)
|
||||||
|
return -1;
|
||||||
|
x = pos(p[3]);
|
||||||
|
if(x >= 0)
|
||||||
|
c += x;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(done < 3)
|
||||||
|
*q++=(c&0x00ff0000)>>16;
|
||||||
|
|
||||||
|
if(done < 2)
|
||||||
|
*q++=(c&0x0000ff00)>>8;
|
||||||
|
if(done < 1)
|
||||||
|
*q++=(c&0x000000ff)>>0;
|
||||||
|
}
|
||||||
|
return q - (unsigned char*)data;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Hgskolan
|
||||||
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||||
|
* 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. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the Kungliga Tekniska
|
||||||
|
* Hgskolan and its contributors.
|
||||||
|
*
|
||||||
|
* 4. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* $Id: base64.h,v 1.1 2005/02/11 07:34:35 jpm Exp jpm $ */
|
||||||
|
|
||||||
|
#ifndef _BASE64_H_
|
||||||
|
#define _BASE64_H_
|
||||||
|
|
||||||
|
int base64_encode(const void *data, int size, char **str);
|
||||||
|
int base64_decode(const char *str, void *data);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,60 @@
|
||||||
|
// This file is part of Jiffy released under the MIT license.
|
||||||
|
// See the LICENSE file for more information.
|
||||||
|
|
||||||
|
#include "emqttd_plugin_mysql_app.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
|
||||||
|
{
|
||||||
|
emqttd_plugin_mysql_app_st* st = enif_alloc(sizeof(emqttd_plugin_mysql_app_st));
|
||||||
|
if(st == NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->atom_ok = make_atom(env, "ok");
|
||||||
|
st->atom_error = make_atom(env, "error");
|
||||||
|
st->atom_null = make_atom(env, "null");
|
||||||
|
st->atom_true = make_atom(env, "true");
|
||||||
|
st->atom_false = make_atom(env, "false");
|
||||||
|
st->atom_bignum = make_atom(env, "bignum");
|
||||||
|
st->atom_bignum_e = make_atom(env, "bignum_e");
|
||||||
|
st->atom_bigdbl = make_atom(env, "bigdbl");
|
||||||
|
st->atom_partial = make_atom(env, "partial");
|
||||||
|
st->atom_uescape = make_atom(env, "uescape");
|
||||||
|
st->atom_pretty = make_atom(env, "pretty");
|
||||||
|
st->atom_force_utf8 = make_atom(env, "force_utf8");
|
||||||
|
|
||||||
|
// Markers used in encoding
|
||||||
|
st->ref_object = make_atom(env, "$object_ref$");
|
||||||
|
st->ref_array = make_atom(env, "$array_ref$");
|
||||||
|
|
||||||
|
*priv = (void*) st;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
|
||||||
|
{
|
||||||
|
return load(env, priv, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unload(ErlNifEnv* env, void* priv)
|
||||||
|
{
|
||||||
|
enif_free(priv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ErlNifFunc funcs[] =
|
||||||
|
{
|
||||||
|
{"nif_pbkdf2_check", 2, pbkdf2_check}
|
||||||
|
};
|
||||||
|
|
||||||
|
ERL_NIF_INIT(emqttd_plugin_mysql_app, funcs, &load, &reload, &upgrade, &unload);
|
|
@ -0,0 +1,44 @@
|
||||||
|
// This file is part of Jiffy released under the MIT license.
|
||||||
|
// See the LICENSE file for more information.
|
||||||
|
|
||||||
|
#ifndef EMQTTD_PLUGIN_MYSQL_APP_H
|
||||||
|
#define EMQTTD_PLUGIN_MYSQL_APP_H
|
||||||
|
|
||||||
|
#include "erl_nif.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ERL_NIF_TERM atom_ok;
|
||||||
|
ERL_NIF_TERM atom_error;
|
||||||
|
ERL_NIF_TERM atom_null;
|
||||||
|
ERL_NIF_TERM atom_true;
|
||||||
|
ERL_NIF_TERM atom_false;
|
||||||
|
ERL_NIF_TERM atom_bignum;
|
||||||
|
ERL_NIF_TERM atom_bignum_e;
|
||||||
|
ERL_NIF_TERM atom_bigdbl;
|
||||||
|
ERL_NIF_TERM atom_partial;
|
||||||
|
ERL_NIF_TERM atom_uescape;
|
||||||
|
ERL_NIF_TERM atom_pretty;
|
||||||
|
ERL_NIF_TERM atom_force_utf8;
|
||||||
|
|
||||||
|
ERL_NIF_TERM ref_object;
|
||||||
|
ERL_NIF_TERM ref_array;
|
||||||
|
} emqttd_plugin_mysql_app_st;
|
||||||
|
|
||||||
|
ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name);
|
||||||
|
ERL_NIF_TERM make_ok(emqttd_plugin_mysql_app_st* st, ErlNifEnv* env, ERL_NIF_TERM data);
|
||||||
|
ERL_NIF_TERM make_error(emqttd_plugin_mysql_app_st* st, ErlNifEnv* env, const char* error);
|
||||||
|
|
||||||
|
ERL_NIF_TERM pbkdf2_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
|
||||||
|
int int_from_hex(const unsigned char* p);
|
||||||
|
int int_to_hex(int val, char* p);
|
||||||
|
int utf8_len(int c);
|
||||||
|
int utf8_esc_len(int c);
|
||||||
|
int utf8_validate(unsigned char* data, size_t size);
|
||||||
|
int utf8_to_unicode(unsigned char* buf, size_t size);
|
||||||
|
int unicode_to_utf8(int c, unsigned char* buf);
|
||||||
|
int unicode_from_pair(int hi, int lo);
|
||||||
|
int unicode_uescape(int c, char* buf);
|
||||||
|
int double_to_shortest(char *buf, size_t size, size_t* len, double val);
|
||||||
|
|
||||||
|
#endif // Included EMQTTD_PLUGIN_MYSQL_APP_H
|
|
@ -0,0 +1,278 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013 Jan-Piet Mens <jpmens()gmail.com>
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include "base64.h"
|
||||||
|
#include "erl_nif.h"
|
||||||
|
#include "emqttd_plugin_mysql_app.h"
|
||||||
|
|
||||||
|
#define KEY_LENGTH 24
|
||||||
|
#define SEPARATOR "$"
|
||||||
|
#define SEPARATOR1 "_"
|
||||||
|
#define TRUE (1)
|
||||||
|
#define FALSE (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Split PBKDF2$... string into their components. The caller must free()
|
||||||
|
* the strings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int detoken(char *pbkstr, char **sha, int *iter, char **salt, char **key)
|
||||||
|
{
|
||||||
|
char *p, *s, *save;
|
||||||
|
int rc = 1;
|
||||||
|
|
||||||
|
save = s = strdup(pbkstr);
|
||||||
|
|
||||||
|
if ((p = strsep(&s, SEPARATOR1)) == NULL)
|
||||||
|
goto out;
|
||||||
|
if (strcmp(p, "pbkdf2") != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if ((p = strsep(&s, SEPARATOR)) == NULL)
|
||||||
|
goto out;
|
||||||
|
*sha = strdup(p);
|
||||||
|
|
||||||
|
if ((p = strsep(&s, SEPARATOR)) == NULL)
|
||||||
|
goto out;
|
||||||
|
*iter = atoi(p);
|
||||||
|
|
||||||
|
if ((p = strsep(&s, SEPARATOR)) == NULL)
|
||||||
|
goto out;
|
||||||
|
*salt = strdup(p);
|
||||||
|
|
||||||
|
if ((p = strsep(&s, SEPARATOR)) == NULL)
|
||||||
|
goto out;
|
||||||
|
*key = strdup(p);
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(save);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
pbkdf2_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
ERL_NIF_TERM ret;
|
||||||
|
ErlNifBinary binps, binhash;
|
||||||
|
emqttd_plugin_mysql_app_st* st = enif_alloc(sizeof(emqttd_plugin_mysql_app_st));
|
||||||
|
if(st == NULL) {
|
||||||
|
return make_atom(env, "alloc_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
st->atom_ok = make_atom(env, "ok");
|
||||||
|
st->atom_error = make_atom(env, "error");
|
||||||
|
st->atom_null = make_atom(env, "null");
|
||||||
|
st->atom_true = make_atom(env, "true");
|
||||||
|
st->atom_false = make_atom(env, "false");
|
||||||
|
st->atom_bignum = make_atom(env, "bignum");
|
||||||
|
st->atom_bignum_e = make_atom(env, "bignum_e");
|
||||||
|
st->atom_bigdbl = make_atom(env, "bigdbl");
|
||||||
|
st->atom_partial = make_atom(env, "partial");
|
||||||
|
st->atom_uescape = make_atom(env, "uescape");
|
||||||
|
st->atom_pretty = make_atom(env, "pretty");
|
||||||
|
st->atom_force_utf8 = make_atom(env, "force_utf8");
|
||||||
|
|
||||||
|
// Markers used in encoding
|
||||||
|
st->ref_object = make_atom(env, "$object_ref$");
|
||||||
|
st->ref_array = make_atom(env, "$array_ref$");
|
||||||
|
|
||||||
|
if(argc != 2) {
|
||||||
|
return make_error(st, env, "Bad args");
|
||||||
|
} else if(!enif_inspect_binary(env, argv[0], &binps)|!enif_inspect_binary(env, argv[1], &binhash)) {
|
||||||
|
return make_error(st, env, "Bad args password or username inspect error");
|
||||||
|
}
|
||||||
|
|
||||||
|
char* password = (char*)binps.data;
|
||||||
|
char* hash = (char*)binhash.data;
|
||||||
|
static char *sha, *salt, *h_pw;
|
||||||
|
int iterations, saltlen, blen;
|
||||||
|
char *b64, *keybuf;
|
||||||
|
unsigned char *out;
|
||||||
|
int match = FALSE;
|
||||||
|
const EVP_MD *evpmd;
|
||||||
|
int keylen, rc;
|
||||||
|
|
||||||
|
if (detoken(hash, &sha, &iterations, &salt, &h_pw) != 0)
|
||||||
|
return match;
|
||||||
|
|
||||||
|
/* Determine key length by decoding base64 */
|
||||||
|
if ((keybuf = malloc(strlen(h_pw) + 1)) == NULL) {
|
||||||
|
return make_error(st, env, "internal_error: Out Of memory");
|
||||||
|
}
|
||||||
|
keylen = base64_decode(h_pw, keybuf);
|
||||||
|
if (keylen < 1) {
|
||||||
|
free(keybuf);
|
||||||
|
return make_atom(env, "false");
|
||||||
|
}
|
||||||
|
free(keybuf);
|
||||||
|
|
||||||
|
if ((out = malloc(keylen)) == NULL) {
|
||||||
|
return make_error(st, env, "Cannot allocate out; out of memory\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PWDEBUG
|
||||||
|
fprintf(stderr, "sha =[%s]\n", sha);
|
||||||
|
fprintf(stderr, "iterations =%d\n", iterations);
|
||||||
|
fprintf(stderr, "salt =[%s]\n", salt);
|
||||||
|
fprintf(stderr, "h_pw =[%s]\n", h_pw);
|
||||||
|
fprintf(stderr, "kenlen =[%d]\n", keylen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
saltlen = strlen((char *)salt);
|
||||||
|
|
||||||
|
evpmd = EVP_sha256();
|
||||||
|
if (strcmp(sha, "sha1") == 0) {
|
||||||
|
evpmd = EVP_sha1();
|
||||||
|
} else if (strcmp(sha, "sha512") == 0) {
|
||||||
|
evpmd = EVP_sha512();
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = PKCS5_PBKDF2_HMAC(password, strlen(password),
|
||||||
|
(unsigned char *)salt, saltlen,
|
||||||
|
iterations,
|
||||||
|
evpmd, keylen, out);
|
||||||
|
if (rc != 1) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
blen = base64_encode(out, keylen, &b64);
|
||||||
|
if (blen > 0) {
|
||||||
|
int i, diff = 0, hlen = strlen(h_pw);
|
||||||
|
#ifdef PWDEBUG
|
||||||
|
fprintf(stderr, "HMAC b64 =[%s]\n", b64);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* "manual" strcmp() to ensure constant time */
|
||||||
|
for (i = 0; (i < blen) && (i < hlen); i++) {
|
||||||
|
diff |= h_pw[i] ^ b64[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
match = diff == 0;
|
||||||
|
if (hlen != blen)
|
||||||
|
match = 0;
|
||||||
|
|
||||||
|
free(b64);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(sha);
|
||||||
|
free(salt);
|
||||||
|
free(h_pw);
|
||||||
|
free(out);
|
||||||
|
|
||||||
|
if(match == 0){
|
||||||
|
ret = make_atom(env, "false");
|
||||||
|
}else{
|
||||||
|
ret = make_atom(env, "true");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pbkdf2_check_native(char *password, char *hash)
|
||||||
|
{
|
||||||
|
static char *sha, *salt, *h_pw;
|
||||||
|
int iterations, saltlen, blen;
|
||||||
|
char *b64;
|
||||||
|
unsigned char key[128];
|
||||||
|
int match = FALSE;
|
||||||
|
const EVP_MD *evpmd;
|
||||||
|
|
||||||
|
if (detoken(hash, &sha, &iterations, &salt, &h_pw) != 0)
|
||||||
|
return match;
|
||||||
|
|
||||||
|
#ifdef PWDEBUG
|
||||||
|
fprintf(stderr, "sha =[%s]\n", sha);
|
||||||
|
fprintf(stderr, "iterations =%d\n", iterations);
|
||||||
|
fprintf(stderr, "salt =[%s]\n", salt);
|
||||||
|
fprintf(stderr, "h_pw =[%s]\n", h_pw);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
saltlen = strlen((char *)salt);
|
||||||
|
|
||||||
|
evpmd = EVP_sha256();
|
||||||
|
if (strcmp(sha, "sha1") == 0) {
|
||||||
|
evpmd = EVP_sha1();
|
||||||
|
} else if (strcmp(sha, "sha512") == 0) {
|
||||||
|
evpmd = EVP_sha512();
|
||||||
|
}
|
||||||
|
|
||||||
|
PKCS5_PBKDF2_HMAC(password, strlen(password),
|
||||||
|
(unsigned char *)salt, saltlen,
|
||||||
|
iterations,
|
||||||
|
evpmd, KEY_LENGTH, key);
|
||||||
|
|
||||||
|
blen = base64_encode(key, KEY_LENGTH, &b64);
|
||||||
|
if (blen > 0) {
|
||||||
|
int i, diff = 0, hlen = strlen(h_pw);
|
||||||
|
#ifdef PWDEBUG
|
||||||
|
fprintf(stderr, "HMAC b64 =[%s]\n", b64);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* "manual" strcmp() to ensure constant time */
|
||||||
|
for (i = 0; (i < blen) && (i < hlen); i++) {
|
||||||
|
diff |= h_pw[i] ^ b64[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
match = diff == 0;
|
||||||
|
if (hlen != blen)
|
||||||
|
match = 0;
|
||||||
|
|
||||||
|
free(b64);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sha);
|
||||||
|
free(salt);
|
||||||
|
free(h_pw);
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// char password[] = "hello";
|
||||||
|
// char PB1[] = "PBKDF2$sha256$10000$eytf9sEo8EprP9P3$2eO6tROHiqI3bm+gg+vpmWooWMpz1zji";
|
||||||
|
char password[] = "supersecret";
|
||||||
|
//char PB1[] = "PBKDF2$sha256$10000$YEbSTt8FaMRDq/ib$Kt97+sMCYg00mqMOBAYinqZlnxX8HqHk";
|
||||||
|
char PB1[] = "pbkdf2_sha256$10000$YEbSTt8FaMRDq/ib$Kt97+sMCYg00mqMOBAYinqZlnxX8HqHk";
|
||||||
|
// char PB1[] = "PBKDF2$sha1$10000$XWfyPLeC9gsD6SbI$HOnjU4Ux7RpeBHdqYxpIGH1R5qCCtNA1";
|
||||||
|
// char PB1[] = "PBKDF2$sha512$10000$v/aaCgBZ+VZN5L8n$BpgjSTyb4weVxr9cA2mvQ+jaCyaAPeYe";
|
||||||
|
int match;
|
||||||
|
|
||||||
|
printf("Checking password [%s] for %s\n", password, PB1);
|
||||||
|
|
||||||
|
match = pbkdf2_check_native(password, PB1);
|
||||||
|
printf("match == %d\n", match);
|
||||||
|
return match;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// This file is part of Jiffy released under the MIT license.
|
||||||
|
// See the LICENSE file for more information.
|
||||||
|
|
||||||
|
#include "emqttd_plugin_mysql_app.h"
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
make_atom(ErlNifEnv* env, const char* name)
|
||||||
|
{
|
||||||
|
ERL_NIF_TERM ret;
|
||||||
|
if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return enif_make_atom(env, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
make_ok(emqttd_plugin_mysql_app_st* st, ErlNifEnv* env, ERL_NIF_TERM value)
|
||||||
|
{
|
||||||
|
return enif_make_tuple2(env, st->atom_ok, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
make_error(emqttd_plugin_mysql_app_st* st, ErlNifEnv* env, const char* error)
|
||||||
|
{
|
||||||
|
return enif_make_tuple2(env, st->atom_error, make_atom(env, error));
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
[
|
||||||
|
{emysql, [
|
||||||
|
{pool, 4},
|
||||||
|
{host, "localhost"},
|
||||||
|
{port, 3306},
|
||||||
|
{username, "root"},
|
||||||
|
{password, "root"},
|
||||||
|
{database, "emqtt"},
|
||||||
|
{encoding, utf8}
|
||||||
|
]},
|
||||||
|
{emqttd_plugin_mysql, [
|
||||||
|
{users_table, auth_user},
|
||||||
|
{acls_table, auth_acl},
|
||||||
|
{field_mapper, [
|
||||||
|
{username, username},
|
||||||
|
{password, password, pbkdf2},
|
||||||
|
{user_super, is_super_user},
|
||||||
|
{acl_username, username},
|
||||||
|
{acl_rw, rw},
|
||||||
|
{acl_topic, topic}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
].
|
|
@ -0,0 +1,23 @@
|
||||||
|
[
|
||||||
|
{emysql, [
|
||||||
|
{pool, 4},
|
||||||
|
{host, "59.188.253.198"},
|
||||||
|
{port, 3306},
|
||||||
|
{username, "root"},
|
||||||
|
{password, "lhroot."},
|
||||||
|
{database, "musicfield"},
|
||||||
|
{encoding, utf8}
|
||||||
|
]},
|
||||||
|
{emqttd_plugin_mysql, [
|
||||||
|
{users_table, auth_user},
|
||||||
|
{acls_table, auth_acl},
|
||||||
|
{field_mapper, [
|
||||||
|
{username, username},
|
||||||
|
{password, password, pbkdf2},
|
||||||
|
{user_super, is_super_user},
|
||||||
|
{acl_username, username},
|
||||||
|
{acl_rw, rw},
|
||||||
|
{acl_topic, topic}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
].
|
|
@ -0,0 +1,112 @@
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%
|
||||||
|
%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%% in the Software without restriction, including without limitation the rights
|
||||||
|
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%% furnished to do so, subject to the following conditions:
|
||||||
|
%%
|
||||||
|
%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%% copies or substantial portions of the Software.
|
||||||
|
%%
|
||||||
|
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%% SOFTWARE.
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% MQTT Broker Header.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Banner
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-define(COPYRIGHT, "Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>").
|
||||||
|
|
||||||
|
-define(LICENSE_MESSAGE, "Licensed under MIT").
|
||||||
|
|
||||||
|
-define(PROTOCOL_VERSION, "MQTT/3.1.1").
|
||||||
|
|
||||||
|
-define(ERTS_MINIMUM, "6.0").
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% PubSub
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-type pubsub() :: publish | subscribe.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% MQTT Topic
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-record(mqtt_topic, {
|
||||||
|
topic :: binary(),
|
||||||
|
node :: node()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type mqtt_topic() :: #mqtt_topic{}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% MQTT Subscriber
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-record(mqtt_subscriber, {
|
||||||
|
topic :: binary(),
|
||||||
|
qos = 0 :: 0 | 1 | 2,
|
||||||
|
pid :: pid()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type mqtt_subscriber() :: #mqtt_subscriber{}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% P2P Queue Subscriber
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-record(mqtt_queue, {
|
||||||
|
name :: binary(),
|
||||||
|
subpid :: pid(),
|
||||||
|
qos = 0 :: 0 | 1 | 2
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type mqtt_queue() :: #mqtt_queue{}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% MQTT Client
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-record(mqtt_client, {
|
||||||
|
clientid :: binary(),
|
||||||
|
username :: binary() | undefined,
|
||||||
|
ipaddr :: inet:ip_address()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type mqtt_client() :: #mqtt_client{}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% MQTT Session
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-record(mqtt_session, {
|
||||||
|
clientid,
|
||||||
|
session_pid,
|
||||||
|
subscriptions = [],
|
||||||
|
awaiting_ack,
|
||||||
|
awaiting_rel
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type mqtt_session() :: #mqtt_session{}.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% MQTT Plugin
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
-record(mqtt_plugin, {
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
attrs,
|
||||||
|
description
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type mqtt_plugin() :: #mqtt_plugin{}.
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,32 @@
|
||||||
|
{port_specs, [
|
||||||
|
{"priv/emqttd_plugin_mysql_app.so", [
|
||||||
|
"c_src/*.c"
|
||||||
|
]}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
{port_env, [
|
||||||
|
{".*", "CXXFLAGS", "$CXXFLAGS -g -Wall -Werror -O3"},
|
||||||
|
|
||||||
|
{"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin)",
|
||||||
|
"LDFLAGS", "$LDFLAGS -lstdc++ -lcrypto"},
|
||||||
|
|
||||||
|
%% OS X Leopard flags for 64-bit
|
||||||
|
{"darwin9.*-64$", "CXXFLAGS", "-m64"},
|
||||||
|
{"darwin9.*-64$", "LDFLAGS", "-arch x86_64"},
|
||||||
|
|
||||||
|
%% OS X Snow Leopard flags for 32-bit
|
||||||
|
{"darwin10.*-32$", "CXXFLAGS", "-m32"},
|
||||||
|
{"darwin10.*-32$", "LDFLAGS", "-arch i386"},
|
||||||
|
|
||||||
|
%% This will merge into basho/rebar/rebar.config eventually
|
||||||
|
{"win32", "CFLAGS", "/Wall /DWIN32 /D_WINDOWS /D_WIN32 /DWINDOWS"},
|
||||||
|
{"win32", "CXXFLAGS", "-g -Wall -O3"}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
|
||||||
|
{eunit_opts, [
|
||||||
|
verbose,
|
||||||
|
{report, {
|
||||||
|
eunit_surefire, [{dir,"."}]
|
||||||
|
}}
|
||||||
|
]}.
|
|
@ -0,0 +1,70 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd demo acl module.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_acl_mysql).
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-behaviour(emqttd_acl_mod).
|
||||||
|
|
||||||
|
%% ACL callbacks
|
||||||
|
-export([init/1, check_acl/2, reload_acl/1, description/0]).
|
||||||
|
-record(state, {user_table, acl_table, acl_username_field, acl_topic_field, acl_rw_field, user_name_field, user_super_field}).
|
||||||
|
|
||||||
|
init(Opts) ->
|
||||||
|
Mapper = proplists:get_value(field_mapper, Opts),
|
||||||
|
State =
|
||||||
|
#state{
|
||||||
|
user_table = proplists:get_value(users_table, Opts, auth_user),
|
||||||
|
user_super_field = proplists:get_value(is_super, Mapper, is_superuser),
|
||||||
|
user_name_field = proplists:get_value(username, Mapper, username),
|
||||||
|
acl_table = proplists:get_value(acls_table, Opts, auth_acl),
|
||||||
|
acl_username_field = proplists:get_value(acl_username, Mapper, username),
|
||||||
|
acl_rw_field = proplists:get_value(acl_rw, Mapper, rw),
|
||||||
|
acl_topic_field = proplists:get_value(acl_topic, Mapper, topic)
|
||||||
|
},
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
check_acl({#mqtt_client{username = Username}, PubSub, Topic}, #state{user_table = UserTab, acl_table = AclTab, user_name_field = UsernameField, user_super_field = SuperField, acl_topic_field = TopicField, acl_username_field = AclUserField, acl_rw_field = AclRwField}) ->
|
||||||
|
Flag = case PubSub of publish -> 2; subscribe -> 1; pubsub -> 2 end,
|
||||||
|
Where = {'and', {'>=', AclRwField, Flag}, {TopicField, Topic}},
|
||||||
|
Where1 = {'or', {AclUserField, Username}, {AclUserField, "*"}},
|
||||||
|
Where2 = {'and', Where, Where1},
|
||||||
|
case emysql:select(UserTab, {'and', {UsernameField, Username}, {SuperField, 1}}) of
|
||||||
|
{ok, []} ->
|
||||||
|
case emysql:select(UserTab, {UsernameField, Username}) of
|
||||||
|
{ok, []} -> ignore;
|
||||||
|
{ok, _} -> case emysql:select(AclTab, Where2) of
|
||||||
|
{ok, []} -> deny;
|
||||||
|
{ok, _Record} -> allow
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
{ok, _} -> allow
|
||||||
|
end.
|
||||||
|
|
||||||
|
reload_acl(_State) -> ok.
|
||||||
|
|
||||||
|
description() -> "ACL Module by Mysql".
|
|
@ -0,0 +1,110 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd authentication by mysql 'user' table.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_auth_mysql).
|
||||||
|
|
||||||
|
-author("Feng Lee <feng@emqtt.io>").
|
||||||
|
|
||||||
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-behaviour(emqttd_auth_mod).
|
||||||
|
|
||||||
|
-export([init/1, check/3, description/0]).
|
||||||
|
|
||||||
|
-define(NOT_LOADED, not_loaded(?LINE)).
|
||||||
|
|
||||||
|
-record(state, {user_table, name_field, pass_field, pass_hash}).
|
||||||
|
|
||||||
|
init(Opts) ->
|
||||||
|
Mapper = proplists:get_value(field_mapper, Opts),
|
||||||
|
{ok, #state{user_table = proplists:get_value(user_table, Opts, auth_user),
|
||||||
|
name_field = proplists:get_value(username, Mapper),
|
||||||
|
pass_field = proplists:get_value(password, Mapper),
|
||||||
|
pass_hash = proplists:get_value(Opts, password_hash)}}.
|
||||||
|
|
||||||
|
check(#mqtt_client{username = undefined}, _Password, _State) ->
|
||||||
|
{error, "Username undefined"};
|
||||||
|
check(_Client, undefined, _State) ->
|
||||||
|
{error, "Password undefined"};
|
||||||
|
check(#mqtt_client{username = Username}, Password,
|
||||||
|
#state{user_table = UserTab, pass_hash = Type,
|
||||||
|
name_field = NameField, pass_field = PassField}) ->
|
||||||
|
Where = {'and', {NameField, Username}, {PassField, hash(Type, Password)}},
|
||||||
|
if Type =:= pbkdf2 ->
|
||||||
|
case emysql:select(UserTab, [PassField], {NameField, Username}) of
|
||||||
|
{ok, []} -> {error, "User not exist"};
|
||||||
|
{ok, Records} ->
|
||||||
|
if length(Records) =:= 1 ->
|
||||||
|
case pbkdf2_check(Password, lists:nth(Records, 1)) of
|
||||||
|
true ->
|
||||||
|
{ok, []};
|
||||||
|
false ->
|
||||||
|
{error, "UserName or Password is invalid"};
|
||||||
|
ErrorInfo ->
|
||||||
|
{error, ErrorInfo}
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
{error, "UserName is ambiguous"}
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
case emysql:select(UserTab, Where) of
|
||||||
|
{ok, []} -> {error, "Username or Password "};
|
||||||
|
{ok, _Record} -> ok
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
description() -> "Authentication by MySQL".
|
||||||
|
|
||||||
|
hash(plain, Password) ->
|
||||||
|
Password;
|
||||||
|
|
||||||
|
hash(md5, Password) ->
|
||||||
|
hexstring(crypto:hash(md5, Password));
|
||||||
|
|
||||||
|
hash(sha, Password) ->
|
||||||
|
hexstring(crypto:hash(sha, Password)).
|
||||||
|
|
||||||
|
hexstring(<<X:128/big-unsigned-integer>>) ->
|
||||||
|
lists:flatten(io_lib:format("~32.16.0b", [X]));
|
||||||
|
|
||||||
|
hexstring(<<X:160/big-unsigned-integer>>) ->
|
||||||
|
lists:flatten(io_lib:format("~40.16.0b", [X])).
|
||||||
|
|
||||||
|
not_loaded(Line) ->
|
||||||
|
erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
|
||||||
|
|
||||||
|
pbkdf2_check(Password, Pbkstr) ->
|
||||||
|
case nif_pbkdf2_check(Password, Pbkstr) of
|
||||||
|
{error, _} = Error ->
|
||||||
|
throw(Error);
|
||||||
|
IOData ->
|
||||||
|
IOData
|
||||||
|
end.
|
||||||
|
|
||||||
|
nif_pbkdf2_check(Password, Pbkstr) ->
|
||||||
|
?NOT_LOADED.
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{application, emqttd_plugin_mysql,
|
||||||
|
[
|
||||||
|
{description, "emqttd MySQL Authentication Plugin"},
|
||||||
|
{vsn, "1.0"},
|
||||||
|
{registered, []},
|
||||||
|
{applications, [
|
||||||
|
kernel,
|
||||||
|
stdlib
|
||||||
|
]},
|
||||||
|
{mod, {emqttd_plugin_mysql_app, []}},
|
||||||
|
{env, []}
|
||||||
|
]}.
|
|
@ -0,0 +1,80 @@
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
||||||
|
%%%
|
||||||
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
%%% in the Software without restriction, including without limitation the rights
|
||||||
|
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
%%% copies of the Software, and to permit persons to whom the Software is
|
||||||
|
%%% furnished to do so, subject to the following conditions:
|
||||||
|
%%%
|
||||||
|
%%% The above copyright notice and this permission notice shall be included in all
|
||||||
|
%%% copies or substantial portions of the Software.
|
||||||
|
%%%
|
||||||
|
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
%%% SOFTWARE.
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
%%% @doc
|
||||||
|
%%% emqttd mysql authentication app.
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%%-----------------------------------------------------------------------------
|
||||||
|
-module(emqttd_plugin_mysql_app).
|
||||||
|
-on_load(init/0).
|
||||||
|
-behaviour(application).
|
||||||
|
%% Application callbacks
|
||||||
|
-export([start/2, prep_stop/1, stop/1, nif_pbkdf2_check/2]).
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
%% Supervisor callbacks
|
||||||
|
-export([init/1]).
|
||||||
|
-define(NOT_LOADED, not_loaded(?LINE)).
|
||||||
|
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Application callbacks
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
start(_StartType, _StartArgs) ->
|
||||||
|
Env = application:get_all_env(),
|
||||||
|
emqttd_access_control:register_mod(auth, emqttd_auth_mysql, Env),
|
||||||
|
emqttd_access_control:register_mod(acl, emqttd_acl_mysql, Env),
|
||||||
|
crypto:start(),
|
||||||
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
|
prep_stop(State) ->
|
||||||
|
emqttd_access_control:unregister_mod(auth, emqttd_auth_mysql), State,
|
||||||
|
emqttd_access_control:unregister_mod(acl, emqttd_acl_mysql), State,
|
||||||
|
crypto:stop().
|
||||||
|
|
||||||
|
stop(_State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
init() ->
|
||||||
|
PrivDir = case code:priv_dir(?MODULE) of
|
||||||
|
{error, _} ->
|
||||||
|
EbinDir = filename:dirname(code:which(?MODULE)),
|
||||||
|
AppPath = filename:dirname(EbinDir),
|
||||||
|
filename:join(AppPath, "priv");
|
||||||
|
Path ->
|
||||||
|
Path
|
||||||
|
end,
|
||||||
|
erlang:load_nif(filename:join(PrivDir, "emqttd_plugin_mysql_app"), 0).
|
||||||
|
|
||||||
|
%%%=============================================================================
|
||||||
|
%%% Supervisor callbacks(Dummy)
|
||||||
|
%%%=============================================================================
|
||||||
|
|
||||||
|
init([]) ->
|
||||||
|
{ok, {{one_for_one, 5, 10}, []}}.
|
||||||
|
|
||||||
|
not_loaded(Line) ->
|
||||||
|
erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
|
||||||
|
|
||||||
|
nif_pbkdf2_check(Password, Hash) ->
|
||||||
|
?NOT_LOADED.
|
Loading…
Reference in New Issue