Coverage Report

Created: 2025-11-14 07:11

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