Skip to content
This repository has been archived by the owner on Mar 12, 2019. It is now read-only.

Commit

Permalink
Cache expensive lookups.
Browse files Browse the repository at this point in the history
	closes #11
  • Loading branch information
jpmens committed Oct 29, 2014
1 parent cccb69e commit f4a6899
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 12 deletions.
5 changes: 3 additions & 2 deletions Makefile.orig
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ BE_CFLAGS =
BE_LDFLAGS =
BE_LDADD =
BE_DEPS =
OBJS = auth-plug.o base64.o pbkdf2-check.o log.o hash.o be-psk.o backends.o
OBJS = auth-plug.o base64.o pbkdf2-check.o log.o hash.o be-psk.o backends.o cache.o

BACKENDS =
BACKENDSTR =
Expand Down Expand Up @@ -102,7 +102,7 @@ auth-plug.so : $(OBJS) $(BE_DEPS)

be-redis.o: be-redis.c be-redis.h log.h hash.h Makefile
be-sqlite.o: be-sqlite.c be-sqlite.h Makefile
auth-plug.o: auth-plug.c be-cdb.h be-mysql.h be-sqlite.h Makefile
auth-plug.o: auth-plug.c be-cdb.h be-mysql.h be-sqlite.h Makefile cache.h
be-psk.o: be-psk.c be-psk.h Makefile
be-cdb.o: be-cdb.c be-cdb.h Makefile
be-mysql.o: be-mysql.c be-mysql.h Makefile
Expand All @@ -113,6 +113,7 @@ base64.o: base64.c base64.h Makefile
log.o: log.c log.h Makefile
hash.o: hash.c hash.h uthash.h Makefile
be-postgres.o: be-postgres.c be-postgres.h Makefile
cache.o: cache.c cache.h uthash.h Makefile

np: np.c base64.o
$(CC) $(CFLAGS) $^ -o $@ $(OSSLIBS)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ The following `auth_opt_` options are supported by the mysql back-end:
| mysql_opt_reconnect | true | | enable MYSQL_OPT_RECONNECT option
| mysql_auto_connect | true | | enable auto_connect function
| anonusername | | | username to use for anonymous connections
| cachetics | 300 | | number of seconds to cache ACL lookups

The SQL query for looking up a user's password hash is mandatory. The query
MUST return a single row only (any other number of rows is considered to be
Expand Down
50 changes: 41 additions & 9 deletions auth-plug.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jan-Piet Mens <jpmens()gmail.com>
* Copyright (c) 2013, 2014 Jan-Piet Mens <jpmens()gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -30,9 +30,11 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <mosquitto.h>
#include <mosquitto_plugin.h>
#include <fnmatch.h>
#include <time.h>

#include "log.h"
#include "hash.h"
Expand All @@ -46,6 +48,8 @@
#include "be-postgres.h"
#include "be-ldap.h"

#include "cache.h"

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

Expand Down Expand Up @@ -78,6 +82,7 @@ struct userdata {
int authentication_be; /* Back-end number user was authenticated in */
int fallback_be; /* Backend to use for anonymous connections */
char *anonusername; /* Configured name of anonymous MQTT user */
time_t cachetics; /* number of seconds to cache ACL lookups */
};

int pbkdf2_check(char *password, char *hash);
Expand All @@ -104,6 +109,8 @@ int mosquitto_auth_plugin_init(void **userdata, struct mosquitto_auth_opt *auth_
char *psk_database = NULL;
#endif

OpenSSL_add_all_algorithms();

*userdata = (struct userdata *)malloc(sizeof(struct userdata));
if (*userdata == NULL) {
perror("allocting userdata");
Expand All @@ -116,6 +123,7 @@ int mosquitto_auth_plugin_init(void **userdata, struct mosquitto_auth_opt *auth_
ud->authentication_be = -1;
ud->fallback_be = -1;
ud->anonusername = NULL;
ud->cachetics = 300;

/*
* Shove all options Mosquitto gives the plugin into a hash,
Expand All @@ -132,6 +140,8 @@ int mosquitto_auth_plugin_init(void **userdata, struct mosquitto_auth_opt *auth_
ud->superusers = strdup(o->value);
if (!strcmp(o->key, "anonusername"))
ud->anonusername = strdup(o->value);
if (!strcmp(o->key, "cachetics"))
ud->cachetics = atol(o->value);
#if 0
if (!strcmp(o->key, "topic_prefix"))
ud->topicprefix = strdup(o->value);
Expand Down Expand Up @@ -394,6 +404,7 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u
struct backend_p **bep;
char *backend_name = NULL;
int match = 0, authorized = FALSE, nord;
int granted = MOSQ_ERR_ACL_DENIED;

if (!username || !*username) { // anonymous users
username = ud->anonusername;
Expand All @@ -406,16 +417,27 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u
access == MOSQ_ACL_READ ? "MOSQ_ACL_READ" : "MOSQ_ACL_WRITE" );


if (!username || !*username || !topic || !*topic)
return MOSQ_ERR_ACL_DENIED;
granted = cache_q(clientid, username, topic, access, ud->cachetics);
if (granted != MOSQ_ERR_UNKNOWN) {
_log(DEBUG, "aclcheck(%s, %s, %d) CACHEDAUTH: %d",
username, topic, access, granted);
return (granted);
}

if (!username || !*username || !topic || !*topic) {
granted = MOSQ_ERR_ACL_DENIED;
goto outout;
}


/* Check for usernames exempt from ACL checking, first */

if (ud->superusers) {
if (fnmatch(ud->superusers, username, 0) == 0) {
_log(DEBUG, "aclcheck(%s, %s, %d) GLOBAL SUPERUSER=Y",
username, topic, access);
return MOSQ_ERR_SUCCESS;
granted = MOSQ_ERR_SUCCESS;
goto outout;
}
}

Expand All @@ -426,7 +448,8 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u
if (match == 1) {
_log(DEBUG, "aclcheck(%s, %s, %d) SUPERUSER=Y by %s",
username, topic, access, b->name);
return MOSQ_ERR_SUCCESS;
granted = MOSQ_ERR_SUCCESS;
goto outout;
}
}

Expand All @@ -442,7 +465,8 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u

if ((nord < 0) || (nord >= NBACKENDS)) {
_log(LOG_NOTICE, "nord is %d: unpossible!", nord);
return (MOSQ_ERR_ACL_DENIED);
granted = MOSQ_ERR_ACL_DENIED;
goto outout;
}

/* FIXME: |-- user bridge was authenticated in back-end 16 (<nil>) */
Expand All @@ -451,8 +475,10 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u


bep = &ud->be_list[nord];
if (nord == -1 || !bep)
return (MOSQ_ERR_ACL_DENIED);
if (nord == -1 || !bep) {
granted = MOSQ_ERR_ACL_DENIED;
goto outout;
}


match = (*bep)->aclcheck((*bep)->conf, clientid, username, topic, access);
Expand All @@ -463,7 +489,13 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u
_log(DEBUG, "aclcheck(%s, %s, %d) AUTHORIZED=%d by %s",
username, topic, access, authorized, backend_name);

return (authorized) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED;
granted = (authorized) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED;

outout: /* goto fail goto fail */

acl_cache(clientid, username, topic, access, granted, ud->cachetics);
return (granted);

}


Expand Down
29 changes: 29 additions & 0 deletions backends.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
/*
* Copyright (c) 2014 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 <string.h>
#include <stdlib.h>
Expand Down
2 changes: 1 addition & 1 deletion be-mysql.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jan-Piet Mens <jpmens()gmail.com>
* Copyright (c) 2013, 2014 Jan-Piet Mens <jpmens()gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down
141 changes: 141 additions & 0 deletions cache.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2014 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <mosquitto.h>
#include "cache.h"
#include <openssl/evp.h>
#include <openssl/sha.h>
#include "uthash.h"

struct aclcache {
char hex[SHA_DIGEST_LENGTH * 2 + 1]; /* key within struct */
int granted;
time_t tics;
UT_hash_handle hh;
};

static struct aclcache *aclcache = NULL;

static unsigned int sha_hash(const char *data, size_t size, unsigned char *out)
{
unsigned int md_len = -1;
const EVP_MD *md = EVP_get_digestbyname("SHA1");

if (md != NULL) {
EVP_MD_CTX mdctx;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit_ex(&mdctx, md, NULL);
EVP_DigestUpdate(&mdctx, data, size);
EVP_DigestFinal_ex(&mdctx, out, &md_len);
EVP_MD_CTX_cleanup(&mdctx);
}
return md_len;
}

static void hexify(const char *clientid, const char *username, const char *topic, int access, char *hex)
{
char *data;
unsigned char hashdata[SHA_DIGEST_LENGTH];
int mdlen, i;

data = malloc(strlen(clientid) + strlen(username) + strlen(topic) + 20);
sprintf(data, "%s:%s:%s:%d", clientid, username, topic, access);

mdlen = sha_hash(data, strlen(data), hashdata);
if (mdlen != SHA_DIGEST_LENGTH) {
free(data);
return;
}

// printf("mdlen=%d, string=%s\n\thash=", mdlen, data);
for (i = 0, *hex = 0; i < sizeof(hashdata) / sizeof(hashdata[0]); i++) {
sprintf(hex + (i*2), "%02X", hashdata[i]);
}
// printf("%s\n", hex);


free(data);
}

/* access is desired read/write access
* granted is what Mosquitto auth-plug actually granted
*/

void acl_cache(const char *clientid, const char *username, const char *topic, int access, int granted, time_t cachetics)
{
char hex[SHA_DIGEST_LENGTH * 2 + 1];
struct aclcache *a;

hexify(clientid, username, topic, access, hex);

HASH_FIND_STR(aclcache, hex, a);
if (a) {
granted = a->granted;

if (time(NULL) > (a->tics + cachetics)) {
printf("EXPIRED!!!!!!!!!!!!!\n");
HASH_DEL(aclcache, a);
}
} else {
a = (struct aclcache *)malloc(sizeof(struct aclcache));
strcpy(a->hex, hex);
a->granted = granted;
a->tics = time(NULL);
HASH_ADD_STR(aclcache, hex, a);
}
}

int cache_q(const char *clientid, const char *username, const char *topic, int access, time_t cachetics)
{
char hex[SHA_DIGEST_LENGTH * 2 + 1];
struct aclcache *a;
int granted = MOSQ_ERR_UNKNOWN;

hexify(clientid, username, topic, access, hex);

HASH_FIND_STR(aclcache, hex, a);
if (a) {
// printf("---> CACHED! %d\n", a->granted);

granted = a->granted;

if (time(NULL) > (a->tics + cachetics)) {
printf("EXPIRED!!!!!!!!!!!!!\n");
HASH_DEL(aclcache, a);
}
}

return (granted);
}


31 changes: 31 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2014 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.
*/

void acl_cache(const char *clientid, const char *username, const char *topic, int access, int granted, time_t cachetics);
int cache_q(const char *clientid, const char *username, const char *topic, int access, time_t cachetics);

0 comments on commit f4a6899

Please sign in to comment.