Coverage Report

Created: 2025-09-04 06:10

/src/mosquitto/src/security_default.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 "mosquitto_broker_internal.h"
26
#include "mosquitto/mqtt_protocol.h"
27
#include "password_file.h"
28
#include "send_mosq.h"
29
#include "util_mosq.h"
30
31
32
int mosquitto_security_init_default(void)
33
3.05k
{
34
3.05k
  int rc;
35
36
  /* Configure plugin identifier */
37
3.05k
  if(db.config->per_listener_settings){
38
0
    for(int i=0; i<db.config->listener_count; i++){
39
0
      db.config->listeners[i].security_options->pid = mosquitto_calloc(1, sizeof(mosquitto_plugin_id_t));
40
0
      if(db.config->listeners[i].security_options->pid == NULL){
41
0
        log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
42
0
        return MOSQ_ERR_NOMEM;
43
0
      }
44
0
      db.config->listeners[i].security_options->pid->plugin_name = mosquitto_strdup("builtin-security");
45
0
      db.config->listeners[i].security_options->pid->listener = &db.config->listeners[i];
46
0
      config__plugin_add_secopt(db.config->listeners[i].security_options->pid, db.config->listeners[i].security_options);
47
0
    }
48
3.05k
  }else{
49
3.05k
    db.config->security_options.pid = mosquitto_calloc(1, sizeof(mosquitto_plugin_id_t));
50
3.05k
    if(db.config->security_options.pid == NULL){
51
0
      log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
52
0
      return MOSQ_ERR_NOMEM;
53
0
    }
54
3.05k
    db.config->security_options.pid->plugin_name = mosquitto_strdup("builtin-security");
55
3.05k
    config__plugin_add_secopt(db.config->security_options.pid, &db.config->security_options);
56
3.05k
  }
57
58
3.05k
  rc = broker_password_file__init();
59
3.05k
  if(rc) return rc;
60
61
2.38k
  rc = broker_acl_file__init();
62
2.38k
  if(rc) return rc;
63
64
1.88k
  rc = psk_file__init();
65
1.88k
  if(rc) return rc;
66
67
1.62k
  return MOSQ_ERR_SUCCESS;
68
1.88k
}
69
70
71
int mosquitto_security_cleanup_default(void)
72
19.6k
{
73
19.6k
  int rc = 0;
74
75
19.6k
  broker_password_file__cleanup();
76
19.6k
  broker_acl_file__cleanup();
77
78
19.6k
  rc = psk_file__cleanup();
79
19.6k
  if(rc != MOSQ_ERR_SUCCESS) return rc;
80
81
19.6k
  if(db.config->per_listener_settings){
82
0
    for(int i=0; i<db.config->listener_count; i++){
83
0
      if(db.config->listeners[i].security_options->pid){
84
0
        mosquitto_FREE(db.config->listeners[i].security_options->pid->plugin_name);
85
0
        mosquitto_FREE(db.config->listeners[i].security_options->pid->config.security_options);
86
0
        mosquitto_FREE(db.config->listeners[i].security_options->pid);
87
0
      }
88
0
    }
89
19.6k
  }else{
90
19.6k
    if(db.config->security_options.pid){
91
3.05k
      mosquitto_FREE(db.config->security_options.pid->plugin_name);
92
3.05k
      mosquitto_FREE(db.config->security_options.pid->config.security_options);
93
3.05k
      mosquitto_FREE(db.config->security_options.pid);
94
3.05k
    }
95
19.6k
  }
96
19.6k
  return MOSQ_ERR_SUCCESS;
97
19.6k
}
98
99
100
#ifdef WITH_TLS
101
static void security__disconnect_auth(struct mosquitto *context)
102
0
{
103
0
  if(context->protocol == mosq_p_mqtt5){
104
0
    send__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL);
105
0
  }
106
0
  mosquitto__set_state(context, mosq_cs_disconnecting);
107
0
  do_disconnect(context, MOSQ_ERR_AUTH);
108
0
}
109
#endif
110
111
/* Apply security settings after a reload.
112
 * Includes:
113
 * - Disconnecting anonymous users if appropriate
114
 * - Disconnecting users with invalid passwords
115
 * - Reapplying ACLs
116
 */
117
int mosquitto_security_apply_default(void)
118
0
{
119
0
  struct mosquitto *context, *ctxt_tmp = NULL;
120
0
  bool allow_anonymous;
121
0
#ifdef WITH_TLS
122
0
  X509_NAME *name;
123
0
  X509_NAME_ENTRY *name_entry;
124
0
  ASN1_STRING *name_asn1 = NULL;
125
0
  struct mosquitto__listener *listener;
126
0
  BIO *subject_bio;
127
0
  char *data_start;
128
0
  size_t name_length;
129
0
  char *subject;
130
0
#endif
131
132
0
#ifdef WITH_TLS
133
0
  for(int i=0; i<db.config->listener_count; i++){
134
0
    listener = &db.config->listeners[i];
135
0
    if(listener && listener->ssl_ctx && listener->certfile && listener->keyfile && listener->crlfile && listener->require_certificate){
136
0
      if(net__tls_server_ctx(listener)){
137
0
        return MOSQ_ERR_TLS;
138
0
      }
139
140
0
      if(net__tls_load_verify(listener)){
141
0
        return MOSQ_ERR_TLS;
142
0
      }
143
0
    }
144
0
  }
145
0
#endif
146
147
0
  HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){
148
0
    if(context->bridge){
149
0
      continue;
150
0
    }
151
152
    /* Check for anonymous clients when allow_anonymous is false */
153
0
    if(db.config->per_listener_settings){
154
0
      if(context->listener){
155
0
        allow_anonymous = context->listener->security_options->allow_anonymous;
156
0
      }else{
157
        /* Client not currently connected, so defer judgement until it does connect */
158
0
        allow_anonymous = true;
159
0
      }
160
0
    }else{
161
0
      allow_anonymous = db.config->security_options.allow_anonymous;
162
0
    }
163
164
0
    if(!allow_anonymous && !context->username){
165
0
      mosquitto__set_state(context, mosq_cs_disconnecting);
166
0
      do_disconnect(context, MOSQ_ERR_AUTH);
167
0
      continue;
168
0
    }
169
170
    /* Check for connected clients that are no longer authorised */
171
0
#ifdef WITH_TLS
172
0
    if(context->listener && context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){
173
      /* Client must have either a valid certificate, or valid PSK used as a username. */
174
0
      if(!context->ssl){
175
0
        if(context->protocol == mosq_p_mqtt5){
176
0
          send__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL);
177
0
        }
178
0
        mosquitto__set_state(context, mosq_cs_disconnecting);
179
0
        do_disconnect(context, MOSQ_ERR_AUTH);
180
0
        continue;
181
0
      }
182
0
#ifdef FINAL_WITH_TLS_PSK
183
0
      if(context->listener->psk_hint){
184
        /* Client should have provided an identity to get this far. */
185
0
        if(!context->username){
186
0
          security__disconnect_auth(context);
187
0
          continue;
188
0
        }
189
0
      }else
190
0
#endif /* FINAL_WITH_TLS_PSK */
191
0
      {
192
        /* Free existing credentials and then recover them. */
193
0
        mosquitto_FREE(context->username);
194
0
        mosquitto_FREE(context->password);
195
196
0
        X509 *client_cert = SSL_get_peer_certificate(context->ssl);
197
0
        if(!client_cert){
198
0
          security__disconnect_auth(context);
199
0
          continue;
200
0
        }
201
0
        name = X509_get_subject_name(client_cert);
202
0
        if(!name){
203
0
          X509_free(client_cert);
204
0
          security__disconnect_auth(context);
205
0
          continue;
206
0
        }
207
0
        if (context->listener->use_identity_as_username) { /* use_identity_as_username */
208
0
          int i = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
209
0
          if(i == -1){
210
0
            X509_free(client_cert);
211
0
            security__disconnect_auth(context);
212
0
            continue;
213
0
          }
214
0
          name_entry = X509_NAME_get_entry(name, i);
215
0
          if(name_entry){
216
0
            name_asn1 = X509_NAME_ENTRY_get_data(name_entry);
217
0
            if (name_asn1 == NULL) {
218
0
              X509_free(client_cert);
219
0
              security__disconnect_auth(context);
220
0
              continue;
221
0
            }
222
0
            context->username = mosquitto_strdup((char *) ASN1_STRING_get0_data(name_asn1));
223
0
            if(!context->username){
224
0
              X509_free(client_cert);
225
0
              security__disconnect_auth(context);
226
0
              continue;
227
0
            }
228
            /* Make sure there isn't an embedded NUL character in the CN */
229
0
            if ((size_t)ASN1_STRING_length(name_asn1) != strlen(context->username)) {
230
0
              X509_free(client_cert);
231
0
              security__disconnect_auth(context);
232
0
              continue;
233
0
            }
234
0
          }
235
0
        } else { /* use_subject_as_username */
236
0
          subject_bio = BIO_new(BIO_s_mem());
237
0
          X509_NAME_print_ex(subject_bio, X509_get_subject_name(client_cert), 0, XN_FLAG_RFC2253);
238
0
          data_start = NULL;
239
0
          name_length = (size_t)BIO_get_mem_data(subject_bio, &data_start);
240
0
          subject = mosquitto_malloc(sizeof(char)*name_length+1);
241
0
          if(!subject){
242
0
            BIO_free(subject_bio);
243
0
            X509_free(client_cert);
244
0
            security__disconnect_auth(context);
245
0
            continue;
246
0
          }
247
0
          memcpy(subject, data_start, name_length);
248
0
          subject[name_length] = '\0';
249
0
          BIO_free(subject_bio);
250
0
          context->username = subject;
251
0
        }
252
0
        if(!context->username){
253
0
          X509_free(client_cert);
254
0
          security__disconnect_auth(context);
255
0
          continue;
256
0
        }
257
0
        X509_free(client_cert);
258
0
      }
259
0
    }else
260
0
#endif
261
0
    {
262
      /* Username/password check only if the identity/subject check not used */
263
0
      if(mosquitto_basic_auth(context) != MOSQ_ERR_SUCCESS){
264
0
        mosquitto__set_state(context, mosq_cs_disconnecting);
265
0
        do_disconnect(context, MOSQ_ERR_AUTH);
266
0
        continue;
267
0
      }
268
0
    }
269
0
  }
270
271
0
  return MOSQ_ERR_SUCCESS;
272
0
}