Convert default security to "plugin" mode.

This converts password_file and acl_file checks to act like a v5 plugin.
pull/1885/head
Roger A. Light 5 years ago
parent 0b414c6fff
commit ef68063985

@ -256,6 +256,7 @@ struct mosquitto__security_options {
char *auto_id_prefix;
int auto_id_prefix_len;
struct plugin__callbacks plugin_callbacks;
mosquitto_plugin_id_t *pid; /* For registering as a "plugin" */
};
struct mosquitto__listener {
@ -838,8 +839,6 @@ int mosquitto_psk_key_get(struct mosquitto_db *db, struct mosquitto *context, co
int mosquitto_security_init_default(struct mosquitto_db *db, bool reload);
int mosquitto_security_apply_default(struct mosquitto_db *db);
int mosquitto_security_cleanup_default(struct mosquitto_db *db, bool reload);
int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *context, const char *topic, int access);
int mosquitto_unpwd_check_default(struct mosquitto_db *db, struct mosquitto *context);
int mosquitto_psk_key_get_default(struct mosquitto_db *db, struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len);
int mosquitto_security_auth_start(struct mosquitto_db *db, struct mosquitto *context, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len);

@ -679,21 +679,24 @@ int mosquitto_acl_check(struct mosquitto_db *db, struct mosquitto *context, cons
if(!context->id){
return MOSQ_ERR_ACL_DENIED;
}
if(context->bridge){
return MOSQ_ERR_SUCCESS;
}
rc = acl__check_dollar(topic, access);
if(rc) return rc;
rc = mosquitto_acl_check_default(db, context, topic, access);
if(rc != MOSQ_ERR_PLUGIN_DEFER){
return rc;
}
/* Default check has accepted or deferred at this point.
/*
* If no plugins exist we should accept at this point so set rc to success.
*/
rc = MOSQ_ERR_SUCCESS;
if(db->config->per_listener_settings){
opts = &context->listener->security_options;
if(context->listener){
opts = &context->listener->security_options;
}else{
return MOSQ_ERR_ACL_DENIED;
}
}else{
opts = &db->config->security_options;
}
@ -747,14 +750,10 @@ int mosquitto_unpwd_check(struct mosquitto_db *db, struct mosquitto *context)
struct mosquitto__security_options *opts;
struct mosquitto_evt_basic_auth event_data;
struct mosquitto__callback *cb_base;
bool plugin_used = false;
rc = MOSQ_ERR_PLUGIN_DEFER;
rc = mosquitto_unpwd_check_default(db, context);
if(rc != MOSQ_ERR_PLUGIN_DEFER){
return rc;
}
/* Default check has accepted or deferred at this point.
* If no plugins exist we should accept at this point so set rc to success.
*/
if(db->config->per_listener_settings){
opts = &context->listener->security_options;
}else{
@ -770,6 +769,7 @@ int mosquitto_unpwd_check(struct mosquitto_db *db, struct mosquitto *context)
if(rc != MOSQ_ERR_PLUGIN_DEFER){
return rc;
}
plugin_used = true;
}
for(i=0; i<opts->auth_plugin_config_count; i++){
@ -781,6 +781,7 @@ int mosquitto_unpwd_check(struct mosquitto_db *db, struct mosquitto *context)
context,
context->username,
context->password);
plugin_used = true;
}else if(opts->auth_plugin_configs[i].plugin.version == 3){
rc = opts->auth_plugin_configs[i].plugin.unpwd_check_v3(
@ -788,26 +789,38 @@ int mosquitto_unpwd_check(struct mosquitto_db *db, struct mosquitto *context)
context,
context->username,
context->password);
plugin_used = true;
}else if(opts->auth_plugin_configs[i].plugin.version == 2){
rc = opts->auth_plugin_configs[i].plugin.unpwd_check_v2(
opts->auth_plugin_configs[i].plugin.user_data,
context->username,
context->password);
plugin_used = true;
}
}
/* If all plugins deferred, this is a denial. If rc == MOSQ_ERR_SUCCESS
* here, then no plugins were configured. Unless we have all deferred, and
* anonymous logins are allowed. */
if(rc == MOSQ_ERR_PLUGIN_DEFER){
if(context->username == NULL &&
((db->config->per_listener_settings && context->listener->security_options.allow_anonymous == true)
|| (!db->config->per_listener_settings && db->config->security_options.allow_anonymous == true))){
if(plugin_used == false){
if((db->config->per_listener_settings && context->listener->security_options.allow_anonymous != false)
|| (!db->config->per_listener_settings && db->config->security_options.allow_anonymous != false)){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_AUTH;
}
}else{
if(rc == MOSQ_ERR_PLUGIN_DEFER){
if(context->username == NULL &&
((db->config->per_listener_settings && context->listener->security_options.allow_anonymous != false)
|| (!db->config->per_listener_settings && db->config->security_options.allow_anonymous != false))){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_AUTH;
}
}
}
return rc;

@ -35,6 +35,8 @@ static int psk__file_parse(struct mosquitto_db *db, struct mosquitto__unpwd **ps
#ifdef WITH_TLS
static int pw__digest(const char *password, const unsigned char *salt, unsigned int salt_len, unsigned char *hash, unsigned int *hash_len, enum mosquitto_pwhash_type hashtype);
#endif
static int mosquitto_unpwd_check_default(int event, void *event_data, void *userdata);
static int mosquitto_acl_check_default(int event, void *event_data, void *userdata);
@ -47,6 +49,24 @@ int mosquitto_security_init_default(struct mosquitto_db *db, bool reload)
UNUSED(reload);
/* Configure plugin identifier */
if(db->config->per_listener_settings){
for(i=0; i<db->config->listener_count; i++){
db->config->listeners[i].security_options.pid = mosquitto__calloc(1, sizeof(mosquitto_plugin_id_t));
if(db->config->listeners[i].security_options.pid == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
db->config->listeners[i].security_options.pid->listener = &db->config->listeners[i];
}
}else{
db->config->security_options.pid = mosquitto__calloc(1, sizeof(mosquitto_plugin_id_t));
if(db->config->security_options.pid == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
/* Load username/password data if required. */
if(db->config->per_listener_settings){
for(i=0; i<db->config->listener_count; i++){
@ -57,6 +77,8 @@ int mosquitto_security_init_default(struct mosquitto_db *db, bool reload)
log__printf(NULL, MOSQ_LOG_ERR, "Error opening password file \"%s\".", pwf);
return rc;
}
mosquitto_callback_register(db->config->listeners[i].security_options.pid,
MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL, NULL);
}
}
}else{
@ -69,6 +91,8 @@ int mosquitto_security_init_default(struct mosquitto_db *db, bool reload)
return rc;
}
}
mosquitto_callback_register(db->config->security_options.pid,
MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL, NULL);
}
}
@ -81,6 +105,8 @@ int mosquitto_security_init_default(struct mosquitto_db *db, bool reload)
log__printf(NULL, MOSQ_LOG_ERR, "Error opening acl file \"%s\".", db->config->listeners[i].security_options.acl_file);
return rc;
}
mosquitto_callback_register(db->config->listeners[i].security_options.pid,
MOSQ_EVT_ACL_CHECK, mosquitto_acl_check_default, NULL, NULL);
}
}
}else{
@ -90,6 +116,8 @@ int mosquitto_security_init_default(struct mosquitto_db *db, bool reload)
log__printf(NULL, MOSQ_LOG_ERR, "Error opening acl file \"%s\".", db->config->security_options.acl_file);
return rc;
}
mosquitto_callback_register(db->config->security_options.pid,
MOSQ_EVT_ACL_CHECK, mosquitto_acl_check_default, NULL, NULL);
}
}
@ -147,6 +175,19 @@ int mosquitto_security_cleanup_default(struct mosquitto_db *db, bool reload)
}
}
if(db->config->per_listener_settings){
for(i=0; i<db->config->listener_count; i++){
if(db->config->listeners[i].security_options.pid){
mosquitto_callback_unregister(db->config->listeners[i].security_options.pid,
MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL);
}
}
}else{
if(db->config->security_options.pid){
mosquitto_callback_unregister(db->config->security_options.pid,
MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL);
}
}
return MOSQ_ERR_SUCCESS;
}
@ -314,8 +355,9 @@ int add__acl_pattern(struct mosquitto__security_options *security_opts, const ch
return MOSQ_ERR_SUCCESS;
}
int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *context, const char *topic, int access)
static int mosquitto_acl_check_default(int event, void *event_data, void *userdata)
{
struct mosquitto_evt_acl_check *ed = event_data;
char *local_acl;
struct mosquitto__acl *acl_root;
bool result;
@ -323,13 +365,15 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte
int len, tlen, clen, ulen;
char *s;
struct mosquitto__security_options *security_opts = NULL;
struct mosquitto_db *db;
if(!db || !context || !topic) return MOSQ_ERR_INVAL;
if(context->bridge) return MOSQ_ERR_SUCCESS;
if(ed->client->bridge) return MOSQ_ERR_SUCCESS;
if(ed->access == MOSQ_ACL_SUBSCRIBE || ed->access == MOSQ_ACL_UNSUBSCRIBE) return MOSQ_ERR_SUCCESS; /* FIXME - implement ACL subscription strings. */
db = mosquitto__get_db();
if(db->config->per_listener_settings){
if(!context->listener) return MOSQ_ERR_ACL_DENIED;
security_opts = &context->listener->security_options;
if(!ed->client->listener) return MOSQ_ERR_ACL_DENIED;
security_opts = &ed->client->listener->security_options;
}else{
security_opts = &db->config->security_options;
}
@ -337,11 +381,10 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte
return MOSQ_ERR_PLUGIN_DEFER;
}
if(access == MOSQ_ACL_SUBSCRIBE || access == MOSQ_ACL_UNSUBSCRIBE) return MOSQ_ERR_SUCCESS; /* FIXME - implement ACL subscription strings. */
if(!context->acl_list && !security_opts->acl_patterns) return MOSQ_ERR_ACL_DENIED;
if(!ed->client->acl_list && !security_opts->acl_patterns) return MOSQ_ERR_ACL_DENIED;
if(context->acl_list){
acl_root = context->acl_list->acl;
if(ed->client->acl_list){
acl_root = ed->client->acl_list->acl;
}else{
acl_root = NULL;
}
@ -351,17 +394,17 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte
/* Loop through the topic looking for matches to this ACL. */
/* If subscription starts with $, acl_root->topic must also start with $. */
if(topic[0] == '$' && acl_root->topic[0] != '$'){
if(ed->topic[0] == '$' && acl_root->topic[0] != '$'){
acl_root = acl_root->next;
continue;
}
mosquitto_topic_matches_sub(acl_root->topic, topic, &result);
mosquitto_topic_matches_sub(acl_root->topic, ed->topic, &result);
if(result){
if(acl_root->access == MOSQ_ACL_NONE){
/* Access was explicitly denied for this topic. */
return MOSQ_ERR_ACL_DENIED;
}
if(access & acl_root->access){
if(ed->access & acl_root->access){
/* And access is allowed. */
return MOSQ_ERR_SUCCESS;
}
@ -379,31 +422,31 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte
* id to bypass ACL checks (or have a username/client id that cannot
* publish or receive messages to its own place in the hierarchy).
*/
if(context->username && strpbrk(context->username, "+#")){
log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", context->username);
if(ed->client->username && strpbrk(ed->client->username, "+#")){
log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", ed->client->username);
return MOSQ_ERR_ACL_DENIED;
}
if(context->id && strpbrk(context->id, "+#")){
log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", context->id);
if(ed->client->id && strpbrk(ed->client->id, "+#")){
log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", ed->client->id);
return MOSQ_ERR_ACL_DENIED;
}
}
/* Loop through all pattern ACLs. ACL denial patterns are iterated over first. */
if(!context->id) return MOSQ_ERR_ACL_DENIED;
clen = strlen(context->id);
if(!ed->client->id) return MOSQ_ERR_ACL_DENIED;
clen = strlen(ed->client->id);
while(acl_root){
tlen = strlen(acl_root->topic);
if(acl_root->ucount && !context->username){
if(acl_root->ucount && !ed->client->username){
acl_root = acl_root->next;
continue;
}
if(context->username){
ulen = strlen(context->username);
if(ed->client->username){
ulen = strlen(ed->client->username);
len = tlen + acl_root->ccount*(clen-2) + acl_root->ucount*(ulen-2);
}else{
ulen = 0;
@ -416,12 +459,12 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte
if(i<tlen-1 && acl_root->topic[i] == '%'){
if(acl_root->topic[i+1] == 'c'){
i++;
strncpy(s, context->id, clen);
strncpy(s, ed->client->id, clen);
s+=clen;
continue;
}else if(context->username && acl_root->topic[i+1] == 'u'){
}else if(ed->client->username && acl_root->topic[i+1] == 'u'){
i++;
strncpy(s, context->username, ulen);
strncpy(s, ed->client->username, ulen);
s+=ulen;
continue;
}
@ -431,14 +474,14 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte
}
local_acl[len] = '\0';
mosquitto_topic_matches_sub(local_acl, topic, &result);
mosquitto_topic_matches_sub(local_acl, ed->topic, &result);
mosquitto__free(local_acl);
if(result){
if(acl_root->access == MOSQ_ACL_NONE){
/* Access was explicitly denied for this topic pattern. */
return MOSQ_ERR_ACL_DENIED;
}
if(access & acl_root->access){
if(ed->access & acl_root->access){
/* And access is allowed. */
return MOSQ_ERR_SUCCESS;
}
@ -918,8 +961,10 @@ static int mosquitto__memcmp_const(const void *a, const void *b, size_t len)
#endif
int mosquitto_unpwd_check_default(struct mosquitto_db *db, struct mosquitto *context)
static int mosquitto_unpwd_check_default(int event, void *event_data, void *userdata)
{
struct mosquitto_db *db;
struct mosquitto_evt_basic_auth *ed = event_data;
struct mosquitto__unpwd *u;
struct mosquitto__unpwd *unpwd_ref;
#ifdef WITH_TLS
@ -927,65 +972,27 @@ int mosquitto_unpwd_check_default(struct mosquitto_db *db, struct mosquitto *con
unsigned int hash_len;
int rc;
#endif
bool allow_anonymous;
char *password_file;
if(!db) return MOSQ_ERR_INVAL;
if(ed->client->username == NULL){
return MOSQ_ERR_PLUGIN_DEFER;
}
db = mosquitto__get_db();
/*
* If allow_anonymous is true, and there is no password file defined, then
* all users are treated as being anonymous and can connect.
*
* If allow_anonymous is false and there is no password file defined, then
* we defer the decision to other plugins (this is a rejection if no other
* plugins are defined)
*
* If allow_anonymous is true, and there is a password file defined, then
* all users with a username must authenticate. All anonymous users are
* allowed to connect. This is a valid mode, because authenticated users
* can be assigned permissions that anonymous users are not.
*
* If allow_anonymous is false, and there is a password file defined, then
* all users with a username must authenticate. All anonymous users are
* defered to other plugins, (this is a rejection if no other plugins are
* defined).
*/
if(db->config->per_listener_settings){
if(context->bridge) return MOSQ_ERR_SUCCESS;
if(!context->listener) return MOSQ_ERR_INVAL;
unpwd_ref = context->listener->security_options.unpwd;
password_file = context->listener->security_options.password_file;
allow_anonymous = context->listener->security_options.allow_anonymous;
if(ed->client->bridge) return MOSQ_ERR_SUCCESS;
if(!ed->client->listener) return MOSQ_ERR_INVAL;
unpwd_ref = ed->client->listener->security_options.unpwd;
}else{
unpwd_ref = db->config->security_options.unpwd;
password_file = db->config->security_options.password_file;
allow_anonymous = db->config->security_options.allow_anonymous;
}
if(context->username){
if(password_file != NULL){
/* Client must authenticate below */
}else{
if(allow_anonymous == true){
/* No password file, so treated as anonymous */
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_PLUGIN_DEFER;
}
}
}else{
if(allow_anonymous){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_PLUGIN_DEFER;
}
}
HASH_FIND(hh, unpwd_ref, context->username, strlen(context->username), u);
HASH_FIND(hh, unpwd_ref, ed->client->username, strlen(ed->client->username), u);
if(u){
if(u->password){
if(context->password){
if(ed->client->password){
#ifdef WITH_TLS
rc = pw__digest(context->password, u->salt, u->salt_len, hash, &hash_len, u->hashtype);
rc = pw__digest(ed->client->password, u->salt, u->salt_len, hash, &hash_len, u->hashtype);
if(rc == MOSQ_ERR_SUCCESS){
if(hash_len == u->password_len && !mosquitto__memcmp_const(u->password, hash, hash_len)){
return MOSQ_ERR_SUCCESS;
@ -996,7 +1003,7 @@ int mosquitto_unpwd_check_default(struct mosquitto_db *db, struct mosquitto *con
return rc;
}
#else
if(!strcmp(u->password, context->password)){
if(!strcmp(u->password, ed->client->password)){
return MOSQ_ERR_SUCCESS;
}
#endif

Loading…
Cancel
Save