Coverage Report

Created: 2026-05-16 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mosquitto/plugins/acl-file/acl_check.c
Line
Count
Source
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){
43
0
    return MOSQ_ERR_SUCCESS;                                                                        /* FIXME - implement ACL subscription strings. */
44
45
0
  }
46
0
  clientid = mosquitto_client_id(ed->client);
47
0
  username = mosquitto_client_username(ed->client);
48
49
0
  if(!data->acl_file && !data->acl_users && !data->acl_patterns){
50
0
    return MOSQ_ERR_PLUGIN_IGNORE;
51
0
  }
52
53
0
  if(username){
54
0
    HASH_FIND(hh, data->acl_users, username, strlen(username), acl_user);
55
0
  }else{
56
0
    acl_user = &data->acl_anon;
57
0
  }
58
0
  if(!acl_user && !data->acl_patterns){
59
0
    return MOSQ_ERR_ACL_DENIED;
60
0
  }
61
62
0
  if(acl_user){
63
0
    acl_root = acl_user->acl;
64
0
  }else{
65
0
    acl_root = NULL;
66
0
  }
67
68
  /* Loop through all ACLs for this client. ACL denials are iterated over first. */
69
0
  while(acl_root){
70
    /* Loop through the topic looking for matches to this ACL. */
71
72
    /* If subscription starts with $, acl_root->topic must also start with $. */
73
0
    if(ed->topic[0] == '$' && acl_root->topic[0] != '$'){
74
0
      acl_root = acl_root->next;
75
0
      continue;
76
0
    }
77
0
    mosquitto_topic_matches_sub(acl_root->topic, ed->topic, &result);
78
0
    if(result){
79
0
      if(acl_root->access == MOSQ_ACL_NONE){
80
        /* Access was explicitly denied for this topic. */
81
0
        return MOSQ_ERR_ACL_DENIED;
82
0
      }
83
0
      if(ed->access & acl_root->access){
84
        /* And access is allowed. */
85
0
        return MOSQ_ERR_SUCCESS;
86
0
      }
87
0
    }
88
0
    acl_root = acl_root->next;
89
0
  }
90
91
0
  acl_root = data->acl_patterns;
92
93
0
  if(acl_root){
94
    /* We are using pattern based acls. Check whether the username or
95
     * client id contains a + or # and if so deny access.
96
     *
97
     * Without this, a malicious client may configure its username/client
98
     * id to bypass ACL checks (or have a username/client id that cannot
99
     * publish or receive messages to its own place in the hierarchy).
100
     */
101
0
    if(username && strpbrk(username, "+#")){
102
0
      mosquitto_log_printf(MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", username);
103
0
      return MOSQ_ERR_ACL_DENIED;
104
0
    }
105
106
0
    if(clientid && strpbrk(clientid, "+#")){
107
0
      mosquitto_log_printf(MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", clientid);
108
0
      return MOSQ_ERR_ACL_DENIED;
109
0
    }
110
0
  }
111
112
  /* Loop through all pattern ACLs. ACL denial patterns are iterated over first. */
113
0
  if(!clientid){
114
0
    return MOSQ_ERR_ACL_DENIED;
115
0
  }
116
117
0
  while(acl_root){
118
0
    if(acl_root->ucount && !username){
119
0
      acl_root = acl_root->next;
120
0
      continue;
121
0
    }
122
123
0
    if(mosquitto_topic_matches_sub_with_pattern(acl_root->topic, ed->topic, clientid, username, &result)){
124
0
      return MOSQ_ERR_ACL_DENIED;
125
0
    }
126
0
    if(result){
127
0
      if(acl_root->access == MOSQ_ACL_NONE){
128
        /* Access was explicitly denied for this topic pattern. */
129
0
        return MOSQ_ERR_ACL_DENIED;
130
0
      }
131
0
      if(ed->access & acl_root->access){
132
        /* And access is allowed. */
133
0
        return MOSQ_ERR_SUCCESS;
134
0
      }
135
0
    }
136
137
0
    acl_root = acl_root->next;
138
0
  }
139
140
0
  return MOSQ_ERR_ACL_DENIED;
141
0
}