Coverage Report

Created: 2025-08-24 06:43

/src/mosquitto/plugins/acl-file/acl_check.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
Copyright (c) 2011-2021 Roger Light <roger@atchoo.org>
3
4
All rights reserved. This program and the accompanying materials
5
are made available under the terms of the Eclipse Public License 2.0
6
and Eclipse Distribution License v1.0 which accompany this distribution.
7
8
The Eclipse Public License is available at
9
   https://www.eclipse.org/legal/epl-2.0/
10
and the Eclipse Distribution License is available at
11
  http://www.eclipse.org/org/documents/edl-v10.php.
12
13
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
14
15
Contributors:
16
   Roger Light - initial implementation and documentation.
17
*/
18
19
#include "config.h"
20
21
#include <ctype.h>
22
#include <stdio.h>
23
#include <string.h>
24
25
#include "acl_file.h"
26
#include "mosquitto.h"
27
28
29
int acl_file__check(int event, void *event_data, void *userdata)
30
0
{
31
0
  struct mosquitto_evt_acl_check *ed = event_data;
32
0
  struct acl__user *acl_user;
33
0
  struct acl__entry *acl_root;
34
0
  bool result;
35
0
  struct acl_file_data *data = userdata;
36
0
  const char *clientid;
37
0
  const char *username;
38
39
0
  UNUSED(event);
40
41
  // FIXME if(ed->client->bridge) return MOSQ_ERR_SUCCESS;
42
0
  if(ed->access == MOSQ_ACL_SUBSCRIBE || ed->access == MOSQ_ACL_UNSUBSCRIBE) return MOSQ_ERR_SUCCESS; /* FIXME - implement ACL subscription strings. */
43
44
0
  clientid = mosquitto_client_id(ed->client);
45
0
  username = mosquitto_client_username(ed->client);
46
47
0
  if(!data->acl_file && !data->acl_users && !data->acl_patterns){
48
0
    return MOSQ_ERR_PLUGIN_IGNORE;
49
0
  }
50
51
0
  if(username){
52
0
    HASH_FIND(hh, data->acl_users, username, strlen(username), acl_user);
53
0
  }else{
54
0
    acl_user = &data->acl_anon;
55
0
  }
56
0
  if(!acl_user && !data->acl_patterns) return MOSQ_ERR_ACL_DENIED;
57
58
0
  if(acl_user){
59
0
    acl_root = acl_user->acl;
60
0
  }else{
61
0
    acl_root = NULL;
62
0
  }
63
64
  /* Loop through all ACLs for this client. ACL denials are iterated over first. */
65
0
  while(acl_root){
66
    /* Loop through the topic looking for matches to this ACL. */
67
68
    /* If subscription starts with $, acl_root->topic must also start with $. */
69
0
    if(ed->topic[0] == '$' && acl_root->topic[0] != '$'){
70
0
      acl_root = acl_root->next;
71
0
      continue;
72
0
    }
73
0
    mosquitto_topic_matches_sub(acl_root->topic, ed->topic, &result);
74
0
    if(result){
75
0
      if(acl_root->access == MOSQ_ACL_NONE){
76
        /* Access was explicitly denied for this topic. */
77
0
        return MOSQ_ERR_ACL_DENIED;
78
0
      }
79
0
      if(ed->access & acl_root->access){
80
        /* And access is allowed. */
81
0
        return MOSQ_ERR_SUCCESS;
82
0
      }
83
0
    }
84
0
    acl_root = acl_root->next;
85
0
  }
86
87
0
  acl_root = data->acl_patterns;
88
89
0
  if(acl_root){
90
    /* We are using pattern based acls. Check whether the username or
91
     * client id contains a + or # and if so deny access.
92
     *
93
     * Without this, a malicious client may configure its username/client
94
     * id to bypass ACL checks (or have a username/client id that cannot
95
     * publish or receive messages to its own place in the hierarchy).
96
     */
97
0
    if(username && strpbrk(username, "+#")){
98
0
      mosquitto_log_printf(MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", username);
99
0
      return MOSQ_ERR_ACL_DENIED;
100
0
    }
101
102
0
    if(clientid && strpbrk(clientid, "+#")){
103
0
      mosquitto_log_printf(MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", clientid);
104
0
      return MOSQ_ERR_ACL_DENIED;
105
0
    }
106
0
  }
107
108
  /* Loop through all pattern ACLs. ACL denial patterns are iterated over first. */
109
0
  if(!clientid) return MOSQ_ERR_ACL_DENIED;
110
111
0
  while(acl_root){
112
0
    if(acl_root->ucount && !username){
113
0
      acl_root = acl_root->next;
114
0
      continue;
115
0
    }
116
117
0
    if(mosquitto_topic_matches_sub_with_pattern(acl_root->topic, ed->topic, clientid, username, &result)){
118
0
      return MOSQ_ERR_ACL_DENIED;
119
0
    }
120
0
    if(result){
121
0
      if(acl_root->access == MOSQ_ACL_NONE){
122
        /* Access was explicitly denied for this topic pattern. */
123
0
        return MOSQ_ERR_ACL_DENIED;
124
0
      }
125
0
      if(ed->access & acl_root->access){
126
        /* And access is allowed. */
127
0
        return MOSQ_ERR_SUCCESS;
128
0
      }
129
0
    }
130
131
0
    acl_root = acl_root->next;
132
0
  }
133
134
0
  return MOSQ_ERR_ACL_DENIED;
135
0
}