// Check an embedded document of the form { "article/#": "r", "article/+/comments": "rw", "ballotbox": "w" } bool be_mongo_check_acl_topics_map(const bson_iter_t *topics, const char *req_topic, int req_access, const char *clientid, const char *username) { bson_iter_t iter; bson_iter_recurse(topics, &iter); bool granted = false; // Loop through mapped topics, allowing for the fact that a two different ACLs may have complementary permissions. while (bson_iter_next(&iter) && !granted) { const char *permitted_topic = bson_iter_key(&iter); bool topic_matches = false; char *expanded; t_expand(clientid, username, permitted_topic, &expanded); if (expanded && *expanded) { mosquitto_topic_matches_sub(expanded, req_topic, &topic_matches); free(expanded); if (topic_matches) { bson_type_t val_type = bson_iter_type(&iter); if (val_type == BSON_TYPE_UTF8) { // NOTE: can req_access be any other value than 1 or 2? // in that case this may not be correct: // e.g. req_access == 3 (rw) -> granted = (3 & 1 > 0) == true const char *permission = bson_iter_utf8(&iter, NULL); if (strcmp(permission, "r") == 0) { granted = (req_access & 1) > 0; } else if (strcmp(permission, "w") == 0) { granted = (req_access & 2) > 0; } else if (strcmp(permission, "rw") == 0) { granted = true; } } } } } return granted; }
// Check an embedded array of the form [ "public/#", "private/myid/#" ] bool be_mongo_check_acl_topics_array(const bson_iter_t *topics, const char *req_topic, const char *clientid, const char *username) { bson_iter_t iter; bson_iter_recurse(topics, &iter); while (bson_iter_next(&iter)) { const char *permitted_topic = bson_iter_utf8(&iter, NULL); bool topic_matches = false; char *expanded; t_expand(clientid, username, permitted_topic, &expanded); if (expanded && *expanded) { mosquitto_topic_matches_sub(expanded, req_topic, &topic_matches); free(expanded); if (topic_matches) { return true; } } } return false; }
int be_pg_aclcheck(void *handle, const char *clientid, const char *username, const char *topic, int acc) { struct pg_backend *conf = (struct pg_backend *)handle; char *v = NULL; int match = 0; bool bf; PGresult *res = NULL; _log( LOG_DEBUG, "USERNAME: %s, TOPIC: %s, acc: %d", username, topic, acc ); if (!conf || !conf->aclquery) return (FALSE); int localacc = htonl(acc); const char *values[2] = {username,(char*)&localacc}; int lengths[2] = {strlen(username),sizeof(localacc)}; int binary[2] = {0,1}; res = PQexecParams(conf->conn, conf->aclquery, 2, NULL, values, lengths, binary, 0); if ( PQresultStatus(res) != PGRES_TUPLES_OK ) { fprintf(stderr, "%s\n", PQresultErrorMessage(res)); match = BACKEND_ERROR; goto out; } if (PQnfields(res) != 1) { fprintf(stderr, "numfields not ok\n"); goto out; } int rec_count = PQntuples(res); int row = 0; for ( row = 0; row < rec_count; row++ ) { if ( (v = PQgetvalue(res,row,0) ) != NULL) { /* Check mosquitto_match_topic. If true, * if true, set match and break out of loop. */ char *expanded; t_expand(clientid, username, v, &expanded); if (expanded && *expanded) { mosquitto_topic_matches_sub(expanded, topic, &bf); match |= bf; _log(LOG_DEBUG, " postgres: topic_matches(%s, %s) == %d", expanded, v, bf); free(expanded); } } if ( match != 0 ) { break; } } out: PQclear(res); return (match); }
int be_pg_aclcheck(void *handle, const char *clientid, const char *username, const char *topic, int acc) { struct pg_backend *conf = (struct pg_backend *)handle; char *v = NULL; int match = BACKEND_DEFER; bool bf; PGresult *res = NULL; _log(LOG_DEBUG, "USERNAME: %s, TOPIC: %s, acc: %d", username, topic, acc); if (!conf || !conf->aclquery) return BACKEND_DEFER; const int buflen = 11; //10 for 2^32 + 1 char accbuffer[buflen]; snprintf(accbuffer, buflen, "%d", acc); const char *values[2] = {username, accbuffer}; int lengths[2] = {strlen(username), buflen}; res = PQexecParams(conf->conn, conf->aclquery, 2, NULL, values, lengths, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "%s\n", PQresultErrorMessage(res)); match = BACKEND_ERROR; //try to reset connection if failing because of database connection lost if(PQstatus(conf->conn) == CONNECTION_BAD){ _log(LOG_NOTICE, "Noticed a postgres connection loss. Trying to reconnect ...\n"); //try to reinitiate the database connection PQreset(conf->conn); } goto out; } if (PQnfields(res) != 1) { fprintf(stderr, "numfields not ok\n"); goto out; } int rec_count = PQntuples(res); int row = 0; for (row = 0; row < rec_count; row++) { if ((v = PQgetvalue(res, row, 0)) != NULL) { /* * Check mosquitto_match_topic. If true, if true, set * match and break out of loop. */ char *expanded; t_expand(clientid, username, v, &expanded); if (expanded && *expanded) { mosquitto_topic_matches_sub(expanded, topic, &bf); if (bf) match = BACKEND_ALLOW; _log(LOG_DEBUG, " postgres: topic_matches(%s, %s) == %d", expanded, v, bf); free(expanded); } } if (match != BACKEND_DEFER) { break; } } out: PQclear(res); return (match); }