diff --git a/README.md b/README.md index a6f124c9..90adda02 100644 --- a/README.md +++ b/README.md @@ -320,8 +320,10 @@ The following `auth_opt_` options are supported by the `http` back-end: | http_aclcheck_uri | | Y | URI for check acl | | http_with_tls | false | N | Use TLS on connect | -If the configured URLs return an HTTP status code == `200`, the authentication / -authorization succeeds, else it fails. +If the configured URLs return an HTTP status code == `2xx`, the authentication / +authorization succeeds. If the status code == `4xx` authentication / +authorization fails. For status code == `5xx` or server unreachable, if no +other backend succeeded, then an error is returned and client is disconnected. | URI-Param | username | password | clientid | topic | acc | | ----------------- | -------- | -------- | -------- | :---: | :-: | diff --git a/auth-plug.c b/auth-plug.c index 6aa4c8a5..726aead7 100644 --- a/auth-plug.c +++ b/auth-plug.c @@ -457,7 +457,7 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u struct userdata *ud = (struct userdata *)userdata; struct backend_p **bep; char *backend_name = NULL; - int match = 0, authorized = FALSE; + int match = 0, authorized = FALSE, has_error = FALSE; int granted = MOSQ_DENY_ACL; if (!username || !*username) { // anonymous users @@ -504,6 +504,10 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u username, topic, access, b->name); granted = MOSQ_ERR_SUCCESS; goto outout; + } else if (match == BACKEND_ERROR) { + _log(LOG_DEBUG, "aclcheck(%s, %s, %d) HAS_ERROR=Y by %s", + username, topic, access, b->name); + has_error = TRUE; } } @@ -521,6 +525,10 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u username, topic, access, b->name); authorized = TRUE; break; + } else if (match == BACKEND_ERROR) { + _log(LOG_DEBUG, "aclcheck(%s, %s, %d) HAS_ERROR=Y by %s", + username, topic, access, b->name); + has_error = TRUE; } } @@ -529,6 +537,11 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u username, topic, access, authorized, backend_name); granted = (authorized) ? MOSQ_ERR_SUCCESS : MOSQ_DENY_ACL; + if (granted == MOSQ_DENY_ACL && has_error) { + _log(LOG_DEBUG, "aclcheck(%s, %s, %d) AUTHORIZED=N HAS_ERROR=Y => ERR_UNKNOWN", + username, topic, access); + granted = MOSQ_ERR_UNKNOWN; + } outout: /* goto fail goto fail */ diff --git a/backends.h b/backends.h index c0d41761..4c260ec3 100644 --- a/backends.h +++ b/backends.h @@ -33,6 +33,9 @@ #ifndef FALSE # define FALSE (0) #endif +#ifndef BACKEND_ERROR +# define BACKEND_ERROR (2) +#endif #ifndef __BACKENDS_H # define __BACKENDS_H diff --git a/be-http.c b/be-http.c index 9983505c..d363c0b9 100644 --- a/be-http.c +++ b/be-http.c @@ -197,11 +197,14 @@ static int http_post(void *handle, char *uri, const char *clientid, const char * re = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &respCode); if (re == CURLE_OK && respCode >= 200 && respCode < 300) { ok = TRUE; + } else if (re == CURLE_OK && respCode >= 500) { + ok = BACKEND_ERROR; } else { //_log(LOG_NOTICE, "http auth fail re=%d respCode=%d", re, respCode); } } else { _log(LOG_DEBUG, "http req fail url=%s re=%s", url, curl_easy_strerror(re)); + ok = BACKEND_ERROR; } curl_easy_cleanup(curl); diff --git a/be-mysql.c b/be-mysql.c index 03be4131..ee7827fd 100644 --- a/be-mysql.c +++ b/be-mysql.c @@ -248,16 +248,16 @@ int be_mysql_superuser(void *handle, const char *username) if (mysql_ping(conf->mysql)) { fprintf(stderr, "%s\n", mysql_error(conf->mysql)); if (!auto_connect(conf)) { - return (FALSE); + return (BACKEND_ERROR); } } if ((u = escape(conf, username, &ulen)) == NULL) - return (FALSE); + return (BACKEND_ERROR); if ((query = malloc(strlen(conf->superquery) + ulen + 128)) == NULL) { free(u); - return (FALSE); + return (BACKEND_ERROR); } sprintf(query, conf->superquery, u); free(u); @@ -266,6 +266,7 @@ int be_mysql_superuser(void *handle, const char *username) if (mysql_query(conf->mysql, query)) { fprintf(stderr, "%s\n", mysql_error(conf->mysql)); + issuper = BACKEND_ERROR; goto out; } @@ -323,16 +324,16 @@ int be_mysql_aclcheck(void *handle, const char *clientid, const char *username, if (mysql_ping(conf->mysql)) { fprintf(stderr, "%s\n", mysql_error(conf->mysql)); if (!auto_connect(conf)) { - return (FALSE); + return (BACKEND_ERROR); } } if ((u = escape(conf, username, &ulen)) == NULL) - return (FALSE); + return (BACKEND_ERROR); if ((query = malloc(strlen(conf->aclquery) + ulen + 128)) == NULL) { free(u); - return (FALSE); + return (BACKEND_ERROR); } sprintf(query, conf->aclquery, u, acc); free(u); @@ -341,6 +342,7 @@ int be_mysql_aclcheck(void *handle, const char *clientid, const char *username, if (mysql_query(conf->mysql, query)) { _log(LOG_NOTICE, "%s", mysql_error(conf->mysql)); + match = BACKEND_ERROR; goto out; } diff --git a/be-postgres.c b/be-postgres.c index 34191412..7d1b26f6 100644 --- a/be-postgres.c +++ b/be-postgres.c @@ -200,6 +200,7 @@ int be_pg_superuser(void *handle, const char *username) if ( PQresultStatus(res) != PGRES_TUPLES_OK ) { fprintf(stderr, "%s\n", PQresultErrorMessage(res)); + issuper = BACKEND_ERROR; goto out; } @@ -266,6 +267,7 @@ int be_pg_aclcheck(void *handle, const char *clientid, const char *username, con if ( PQresultStatus(res) != PGRES_TUPLES_OK ) { fprintf(stderr, "%s\n", PQresultErrorMessage(res)); + match = BACKEND_ERROR; goto out; } diff --git a/be-redis.c b/be-redis.c index b7599876..5c3cebf8 100644 --- a/be-redis.c +++ b/be-redis.c @@ -34,6 +34,7 @@ #include #include "log.h" #include "hash.h" +#include "backends.h" #include struct redis_backend { @@ -181,7 +182,7 @@ int be_redis_aclcheck(void *handle, const char *clientid, const char *username, r = redisCommand(conf->redis, query, username, acc); if (r == NULL || conf->redis->err != REDIS_OK) { be_redis_reconnect(conf); - return 0; + return BACKEND_ERROR; } free(query); diff --git a/cache.c b/cache.c index 934349f1..ee50b1d4 100644 --- a/cache.c +++ b/cache.c @@ -106,7 +106,7 @@ void acl_cache(const char *clientid, const char *username, const char *topic, in HASH_FIND_STR(ud->aclcache, hex, a); if (a) { - granted = a->granted; + a->granted = granted; if (time(NULL) > (a->seconds + cacheseconds)) { _log(LOG_DEBUG, " Expired [%s] for (%s,%s,%d)", hex, clientid, username, access);