You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mosquitto/src/handle_connect.c

1069 lines
33 KiB
C

/*
Copyright (c) 2009-2021 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <utlist.h>
#include "mosquitto_broker_internal.h"
#include "mqtt_protocol.h"
#include "memory_mosq.h"
#include "packet_mosq.h"
#include "property_mosq.h"
#include "send_mosq.h"
#include "sys_tree.h"
#include "time_mosq.h"
#include "tls_mosq.h"
#include "util_mosq.h"
#include "will_mosq.h"
#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS
# include <libwebsockets.h>
#endif
static char nibble_to_hex(uint8_t value)
{
if(value < 0x0A){
return (char)('0'+value);
}else{
return (char)(65 /*'A'*/ +value-10);
}
}
static char *client_id_gen(uint16_t *idlen, const char *auto_id_prefix, uint16_t auto_id_prefix_len)
{
char *client_id;
uint8_t rnd[16];
int i;
int pos;
if(util__random_bytes(rnd, 16)) return NULL;
*idlen = (uint16_t)(auto_id_prefix_len + 36);
client_id = (char *)mosquitto__calloc((size_t)(*idlen) + 1, sizeof(char));
if(!client_id){
return NULL;
}
if(auto_id_prefix){
memcpy(client_id, auto_id_prefix, auto_id_prefix_len);
}
pos = 0;
for(i=0; i<16; i++){
client_id[auto_id_prefix_len + pos + 0] = nibble_to_hex(rnd[i] & 0x0F);
client_id[auto_id_prefix_len + pos + 1] = nibble_to_hex((rnd[i] >> 4) & 0x0F);
pos += 2;
if(pos == 8 || pos == 13 || pos == 18 || pos == 23){
client_id[auto_id_prefix_len + pos] = '-';
pos++;
}
}
return client_id;
}
/* Remove any queued messages that are no longer allowed through ACL,
* assuming a possible change of username. */
static void connection_check_acl(struct mosquitto *context, struct mosquitto__client_msg **head)
{
struct mosquitto__client_msg *msg_tail, *tmp;
int access;
DL_FOREACH_SAFE((*head), msg_tail, tmp){
if(msg_tail->direction == mosq_md_out){
access = MOSQ_ACL_READ;
}else{
access = MOSQ_ACL_WRITE;
}
if(mosquitto_acl_check(context, msg_tail->base_msg->msg.topic,
msg_tail->base_msg->msg.payloadlen, msg_tail->base_msg->msg.payload,
msg_tail->base_msg->msg.qos, msg_tail->base_msg->msg.retain, access) != MOSQ_ERR_SUCCESS){
DL_DELETE((*head), msg_tail);
db__msg_store_ref_dec(&msg_tail->base_msg);
mosquitto__FREE(msg_tail);
}
}
}
int connect__on_authorised(struct mosquitto *context, void *auth_data_out, uint16_t auth_data_out_len)
{
struct mosquitto *found_context;
struct mosquitto__subleaf *leaf;
mosquitto_property *connack_props = NULL;
uint8_t connect_ack = 0;
int i;
int rc;
int in_quota, out_quota;
uint16_t in_maximum, out_maximum;
/* Find if this client already has an entry. This must be done *after* any security checks. */
HASH_FIND(hh_id, db.contexts_by_id, context->id, strlen(context->id), found_context);
if(found_context){
/* Found a matching client */
if(!net__is_connected(found_context)){
/* Client is reconnecting after a disconnect */
/* FIXME - does anything need to be done here? */
}else{
/* Client is already connected, disconnect old version. This is
* done in context__cleanup() below. */
}
if(context->clean_start == true){
sub__clean_session(found_context);
found_context->session_expiry_interval = 0;
plugin_persist__handle_client_delete(found_context);
}
context->is_persisted = found_context->is_persisted;
found_context->is_persisted = false; /* stops persistence for context being removed */
if(context->clean_start == false && found_context->session_expiry_interval > 0){
if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){
connect_ack |= 0x01;
}
if(found_context->msgs_in.inflight || found_context->msgs_in.queued
|| found_context->msgs_out.inflight || found_context->msgs_out.queued){
in_quota = context->msgs_in.inflight_quota;
out_quota = context->msgs_out.inflight_quota;
in_maximum = context->msgs_in.inflight_maximum;
out_maximum = context->msgs_out.inflight_maximum;
memcpy(&context->msgs_in, &found_context->msgs_in, sizeof(struct mosquitto_msg_data));
memcpy(&context->msgs_out, &found_context->msgs_out, sizeof(struct mosquitto_msg_data));
memset(&found_context->msgs_in, 0, sizeof(struct mosquitto_msg_data));
memset(&found_context->msgs_out, 0, sizeof(struct mosquitto_msg_data));
context->msgs_in.inflight_quota = in_quota;
context->msgs_out.inflight_quota = out_quota;
context->msgs_in.inflight_maximum = in_maximum;
context->msgs_out.inflight_maximum = out_maximum;
db__message_reconnect_reset(context);
}
context->subs = found_context->subs;
found_context->subs = NULL;
context->subs_capacity = found_context->subs_capacity;
context->subs_count = found_context->subs_count;
found_context->subs_capacity = 0;
found_context->subs_count = 0;
context->last_mid = found_context->last_mid;
for(i=0; i<context->subs_capacity; i++){
if(context->subs[i]){
leaf = context->subs[i]->hier->subs;
while(leaf){
if(leaf->context == found_context){
leaf->context = context;
}
leaf = leaf->next;
}
if(context->subs[i]->shared){
leaf = context->subs[i]->shared->subs;
while(leaf){
if(leaf->context == found_context){
leaf->context = context;
}
leaf = leaf->next;
}
}
}
}
}
if((found_context->protocol == mosq_p_mqtt5 && found_context->session_expiry_interval == 0)
|| (found_context->protocol != mosq_p_mqtt5 && found_context->clean_start == true)
|| (context->clean_start == true)
){
context__send_will(found_context);
}
session_expiry__remove(found_context);
will_delay__remove(found_context);
will__clear(found_context);
found_context->clean_start = true;
found_context->session_expiry_interval = 0;
mosquitto__set_state(found_context, mosq_cs_duplicate);
if(found_context->protocol == mosq_p_mqtt5){
send__disconnect(found_context, MQTT_RC_SESSION_TAKEN_OVER, NULL);
}
do_disconnect(found_context, MOSQ_ERR_SESSION_TAKEN_OVER);
}
if(db.config->global_max_clients > 0 && HASH_CNT(hh_id, db.contexts_by_id) >= (unsigned int)db.config->global_max_clients){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_SERVER_BUSY, NULL);
}
rc = MOSQ_ERR_INVAL;
goto error;
}
rc = acl__find_acls(context);
if(rc){
SAFE_FREE(auth_data_out);
return rc;
}
if(db.config->connection_messages == true){
if(context->is_bridge){
if(context->username){
log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s:%d as %s (p%d, c%d, k%d, u'%s').",
context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive, context->username);
}else{
log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s:%d as %s (p%d, c%d, k%d).",
context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive);
}
}else{
if(context->username){
log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s:%d as %s (p%d, c%d, k%d, u'%s').",
context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive, context->username);
}else{
log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s:%d as %s (p%d, c%d, k%d).",
context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive);
}
}
if(context->will) {
log__printf(NULL, MOSQ_LOG_DEBUG, "Will message specified (%ld bytes) (r%d, q%d).",
(long)context->will->msg.payloadlen,
context->will->msg.retain,
context->will->msg.qos);
log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", context->will->msg.topic);
} else {
log__printf(NULL, MOSQ_LOG_DEBUG, "No will message specified.");
}
}
#ifdef WITH_TLS
if(context->ssl){
log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s negotiated %s cipher %s",
context->id,
SSL_get_cipher_version(context->ssl),
SSL_get_cipher_name(context->ssl));
}
#endif
context->ping_t = 0;
context->is_dropping = false;
connection_check_acl(context, &context->msgs_in.inflight);
connection_check_acl(context, &context->msgs_in.queued);
connection_check_acl(context, &context->msgs_out.inflight);
connection_check_acl(context, &context->msgs_out.queued);
context__add_to_by_id(context);
#ifdef WITH_PERSISTENCE
if(!context->clean_start){
db.persistence_changes++;
}
#endif
context->max_qos = context->listener->max_qos;
if(db.config->max_keepalive &&
(context->keepalive > db.config->max_keepalive || context->keepalive == 0)){
keepalive__remove(context);
context->keepalive = db.config->max_keepalive;
keepalive__add(context);
if(context->protocol == mosq_p_mqtt5){
if(mosquitto_property_add_int16(&connack_props, MQTT_PROP_SERVER_KEEP_ALIVE, context->keepalive)){
rc = MOSQ_ERR_NOMEM;
goto error;
}
}else{
send__connack(context, connect_ack, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL);
rc = MOSQ_ERR_INVAL;
goto error;
}
}
if(context->protocol == mosq_p_mqtt5){
if(context->listener->max_topic_alias > 0){
if(mosquitto_property_add_int16(&connack_props, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, context->listener->max_topic_alias)){
rc = MOSQ_ERR_NOMEM;
goto error;
}
}
if(context->assigned_id){
if(mosquitto_property_add_string(&connack_props, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, context->id)){
rc = MOSQ_ERR_NOMEM;
goto error;
}
}
if(context->auth_method){
if(mosquitto_property_add_string(&connack_props, MQTT_PROP_AUTHENTICATION_METHOD, context->auth_method)){
rc = MOSQ_ERR_NOMEM;
goto error;
}
if(auth_data_out && auth_data_out_len > 0){
if(mosquitto_property_add_binary(&connack_props, MQTT_PROP_AUTHENTICATION_DATA, auth_data_out, auth_data_out_len)){
rc = MOSQ_ERR_NOMEM;
goto error;
}
}
}
}
SAFE_FREE(auth_data_out);
mosquitto__set_state(context, mosq_cs_active);
rc = send__connack(context, connect_ack, CONNACK_ACCEPTED, connack_props);
mosquitto_property_free_all(&connack_props);
if(rc) return rc;
db__expire_all_messages(context);
rc = db__message_write_queued_out(context);
if(rc) return rc;
rc = db__message_write_inflight_out_all(context);
if(rc == MOSQ_ERR_SUCCESS){
plugin__handle_connect(context);
if(context->session_expiry_interval != 0){
plugin_persist__handle_client_add(context);
}
}
return rc;
error:
SAFE_FREE(auth_data_out);
mosquitto_property_free_all(&connack_props);
return rc;
}
static int will__read(struct mosquitto *context, const char *client_id, struct mosquitto_message_all **will, uint8_t will_qos, int will_retain)
{
int rc = MOSQ_ERR_SUCCESS;
size_t slen;
uint16_t tlen;
struct mosquitto_message_all *will_struct = NULL;
char *will_topic_mount = NULL;
uint16_t payloadlen;
mosquitto_property *properties = NULL;
will_struct = mosquitto__calloc(1, sizeof(struct mosquitto_message_all));
if(!will_struct){
rc = MOSQ_ERR_NOMEM;
goto error_cleanup;
}
if(context->protocol == PROTOCOL_VERSION_v5){
rc = property__read_all(CMD_WILL, &context->in_packet, &properties);
if(rc) goto error_cleanup;
rc = property__process_will(context, will_struct, &properties);
mosquitto_property_free_all(&properties);
if(rc) goto error_cleanup;
}
rc = packet__read_string(&context->in_packet, &will_struct->msg.topic, &tlen);
if(rc) goto error_cleanup;
if(!tlen){
rc = MOSQ_ERR_PROTOCOL;
goto error_cleanup;
}
if(context->listener->mount_point){
slen = strlen(context->listener->mount_point) + strlen(will_struct->msg.topic) + 1;
will_topic_mount = mosquitto__malloc(slen+1);
if(!will_topic_mount){
rc = MOSQ_ERR_NOMEM;
goto error_cleanup;
}
snprintf(will_topic_mount, slen, "%s%s", context->listener->mount_point, will_struct->msg.topic);
will_topic_mount[slen] = '\0';
mosquitto__FREE(will_struct->msg.topic);
will_struct->msg.topic = will_topic_mount;
}
rc = mosquitto_pub_topic_check(will_struct->msg.topic);
if(rc) goto error_cleanup;
rc = packet__read_uint16(&context->in_packet, &payloadlen);
if(rc) goto error_cleanup;
will_struct->msg.payloadlen = payloadlen;
if(will_struct->msg.payloadlen > 0){
if(db.config->message_size_limit && will_struct->msg.payloadlen > (int)db.config->message_size_limit){
log__printf(NULL, MOSQ_LOG_DEBUG, "Client %s connected with too large Will payload", client_id);
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_PACKET_TOO_LARGE, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);
}
rc = MOSQ_ERR_PAYLOAD_SIZE;
goto error_cleanup;
}
will_struct->msg.payload = mosquitto__malloc((size_t)will_struct->msg.payloadlen);
if(!will_struct->msg.payload){
rc = MOSQ_ERR_NOMEM;
goto error_cleanup;
}
rc = packet__read_bytes(&context->in_packet, will_struct->msg.payload, (uint32_t)will_struct->msg.payloadlen);
if(rc) goto error_cleanup;
}
will_struct->msg.qos = will_qos;
will_struct->msg.retain = will_retain;
*will = will_struct;
return MOSQ_ERR_SUCCESS;
error_cleanup:
if(will_struct){
mosquitto__FREE(will_struct->msg.topic);
mosquitto__FREE(will_struct->msg.payload);
mosquitto_property_free_all(&will_struct->properties);
mosquitto__FREE(will_struct);
}
return rc;
}
static int check_protocol_version(struct mosquitto__listener *listener, int protocol_version)
{
/* Allow bridge protocol as well. */
protocol_version &= 0x7F;
if((protocol_version == 3 && listener->disable_protocol_v3 == false)
|| (protocol_version == 4 && listener->disable_protocol_v4 == false)
|| (protocol_version == 5 && listener->disable_protocol_v5 == false)
){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOT_SUPPORTED;
}
}
#ifdef WITH_TLS
static int get_username_from_cert(struct mosquitto *context)
{
int i;
X509 *client_cert = NULL;
X509_NAME *name;
X509_NAME_ENTRY *name_entry;
ASN1_STRING *name_asn1 = NULL;
BIO *subject_bio;
char *data_start;
long name_length;
char *subject;
client_cert = SSL_get_peer_certificate(context->ssl);
if(!client_cert){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL);
}
return MOSQ_ERR_AUTH;
}
name = X509_get_subject_name(client_cert);
if(!name){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL);
}
X509_free(client_cert);
return MOSQ_ERR_AUTH;
}
if(context->listener->use_identity_as_username){ /* use_identity_as_username */
i = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
if(i == -1){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL);
}
X509_free(client_cert);
return MOSQ_ERR_AUTH;
}
name_entry = X509_NAME_get_entry(name, i);
if(name_entry){
name_asn1 = X509_NAME_ENTRY_get_data(name_entry);
if (name_asn1 == NULL) {
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL);
}
X509_free(client_cert);
return MOSQ_ERR_AUTH;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
context->username = mosquitto__strdup((char *) ASN1_STRING_data(name_asn1));
#else
context->username = mosquitto__strdup((char *) ASN1_STRING_get0_data(name_asn1));
#endif
if(!context->username){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_SERVER_UNAVAILABLE, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_SERVER_UNAVAILABLE, NULL);
}
X509_free(client_cert);
return MOSQ_ERR_NOMEM;
}
/* Make sure there isn't an embedded NUL character in the CN */
if ((size_t)ASN1_STRING_length(name_asn1) != strlen(context->username)) {
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL);
}
X509_free(client_cert);
return MOSQ_ERR_AUTH;
}
}
} else { /* use_subject_as_username */
subject_bio = BIO_new(BIO_s_mem());
X509_NAME_print_ex(subject_bio, X509_get_subject_name(client_cert), 0, XN_FLAG_RFC2253);
data_start = NULL;
name_length = BIO_get_mem_data(subject_bio, &data_start);
subject = mosquitto__malloc(sizeof(char)*(size_t)(name_length+1));
if(!subject){
BIO_free(subject_bio);
X509_free(client_cert);
return MOSQ_ERR_NOMEM;
}
memcpy(subject, data_start, (size_t)name_length);
subject[name_length] = '\0';
BIO_free(subject_bio);
context->username = subject;
}
if(!context->username){
X509_free(client_cert);
return MOSQ_ERR_AUTH;
}
X509_free(client_cert);
client_cert = NULL;
return MOSQ_ERR_SUCCESS;
}
#endif
int handle__connect(struct mosquitto *context)
{
char protocol_name[7];
uint8_t protocol_version;
uint8_t connect_flags;
char *client_id = NULL;
struct mosquitto *found_context;
struct mosquitto_message_all *will_struct = NULL;
uint8_t will, will_retain, will_qos, clean_start;
uint8_t username_flag, password_flag;
char *username = NULL, *password = NULL;
int rc;
uint16_t slen;
mosquitto_property *properties = NULL;
void *auth_data = NULL;
uint16_t auth_data_len = 0;
void *auth_data_out = NULL;
uint16_t auth_data_out_len = 0;
bool allow_zero_length_clientid;
G_CONNECTION_COUNT_INC();
if(!context->listener){
return MOSQ_ERR_INVAL;
}
/* Don't accept multiple CONNECT commands. */
if(context->state != mosq_cs_new){
log__printf(NULL, MOSQ_LOG_NOTICE, "Bad client %s sending multiple CONNECT messages.", context->id);
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
/* Read protocol name as length then bytes rather than with read_string
* because the length is fixed and we can check that. Removes the need
* for another malloc as well. */
if(packet__read_uint16(&context->in_packet, &slen)){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if(slen != 4 /* MQTT */ && slen != 6 /* MQIsdp */){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if(packet__read_bytes(&context->in_packet, protocol_name, slen)){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
protocol_name[slen] = '\0';
if(packet__read_byte(&context->in_packet, &protocol_version)){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if(check_protocol_version(context->listener, protocol_version)){
if(protocol_version == 3 || protocol_version == 4){
context->protocol = mosq_p_mqtt311;
send__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL);
}else{
context->protocol = mosq_p_mqtt5;
send__connack(context, 0, MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION, NULL);
}
rc = MOSQ_ERR_NOT_SUPPORTED;
goto handle_connect_error;
}
if(!strcmp(protocol_name, PROTOCOL_NAME_v31)){
if((protocol_version&0x7F) != PROTOCOL_VERSION_v31){
if(db.config->connection_messages == true){
log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol version %d in CONNECT from %s.",
protocol_version, context->address);
}
send__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL);
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
context->protocol = mosq_p_mqtt31;
if((protocol_version&0x80) == 0x80){
context->is_bridge = true;
}
}else if(!strcmp(protocol_name, PROTOCOL_NAME)){
if((protocol_version&0x7F) == PROTOCOL_VERSION_v311){
context->protocol = mosq_p_mqtt311;
if((protocol_version&0x80) == 0x80){
context->is_bridge = true;
}
}else if((protocol_version&0x7F) == PROTOCOL_VERSION_v5){
context->protocol = mosq_p_mqtt5;
}else{
if(db.config->connection_messages == true){
log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol version %d in CONNECT from %s.",
protocol_version, context->address);
}
send__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL);
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if((context->in_packet.command&0x0F) != 0x00){
/* Reserved flags not set to 0, must disconnect. */
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
}else{
if(db.config->connection_messages == true){
log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol \"%s\" in CONNECT from %s.",
protocol_name, context->address);
}
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if((protocol_version&0x7F) != PROTOCOL_VERSION_v31 && context->in_packet.command != CMD_CONNECT){
return MOSQ_ERR_MALFORMED_PACKET;
}
if(packet__read_byte(&context->in_packet, &connect_flags)){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){
if((connect_flags & 0x01) != 0x00){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
}
clean_start = (connect_flags & 0x02) >> 1;
/* session_expiry_interval will be overriden if the properties are read later */
if(clean_start == false && protocol_version != PROTOCOL_VERSION_v5){
/* v3* has clean_start == false mean the session never expires */
context->session_expiry_interval = UINT32_MAX;
}else{
context->session_expiry_interval = 0;
}
will = connect_flags & 0x04;
will_qos = (connect_flags & 0x18) >> 3;
if(will_qos == 3){
log__printf(NULL, MOSQ_LOG_INFO, "Invalid Will QoS in CONNECT from %s.",
context->address);
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
will_retain = ((connect_flags & 0x20) == 0x20);
password_flag = connect_flags & 0x40;
username_flag = connect_flags & 0x80;
if(will && will_retain && db.config->retain_available == false){
if(protocol_version == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL);
}
rc = MOSQ_ERR_NOT_SUPPORTED;
goto handle_connect_error;
}
/* _remove here because net__socket_accept() uses _add and we must have the
* correct keepalive value */
keepalive__remove(context);
if(packet__read_uint16(&context->in_packet, &(context->keepalive))){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
keepalive__add(context);
if(protocol_version == PROTOCOL_VERSION_v5){
rc = property__read_all(CMD_CONNECT, &context->in_packet, &properties);
if(rc == MOSQ_ERR_DUPLICATE_PROPERTY || rc == MOSQ_ERR_PROTOCOL){
send__connack(context, 0, MQTT_RC_PROTOCOL_ERROR, NULL);
}else if(rc == MOSQ_ERR_MALFORMED_PACKET){
send__connack(context, 0, MQTT_RC_MALFORMED_PACKET, NULL);
}
if(rc) goto handle_connect_error;
}
rc = property__process_connect(context, &properties);
if(rc == MOSQ_ERR_PROTOCOL){
send__connack(context, 0, MQTT_RC_PROTOCOL_ERROR, NULL);
goto handle_connect_error;
}
if(will && will_qos > context->listener->max_qos){
if(protocol_version == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_QOS_NOT_SUPPORTED, NULL);
}
rc = MOSQ_ERR_NOT_SUPPORTED;
goto handle_connect_error;
}
mosquitto_property_read_string(properties, MQTT_PROP_AUTHENTICATION_METHOD, &context->auth_method, false);
mosquitto_property_read_binary(properties, MQTT_PROP_AUTHENTICATION_DATA, &auth_data, &auth_data_len, false);
mosquitto_property_free_all(&properties);
if(auth_data && !context->auth_method){
send__connack(context, 0, MQTT_RC_PROTOCOL_ERROR, NULL);
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if(packet__read_string(&context->in_packet, &client_id, &slen)){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
if(slen == 0){
if(context->protocol == mosq_p_mqtt31){
send__connack(context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL);
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}else{ /* mqtt311/mqtt5 */
mosquitto__FREE(client_id);
if(db.config->per_listener_settings){
allow_zero_length_clientid = context->listener->security_options->allow_zero_length_clientid;
}else{
allow_zero_length_clientid = db.config->security_options.allow_zero_length_clientid;
}
if((context->protocol == mosq_p_mqtt311 && clean_start == 0) || allow_zero_length_clientid == false){
if(context->protocol == mosq_p_mqtt311){
send__connack(context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL);
}else{
send__connack(context, 0, MQTT_RC_UNSPECIFIED, NULL);
}
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}else{
if(db.config->per_listener_settings){
client_id = client_id_gen(&slen, context->listener->security_options->auto_id_prefix, context->listener->security_options->auto_id_prefix_len);
}else{
client_id = client_id_gen(&slen, db.config->security_options.auto_id_prefix, db.config->security_options.auto_id_prefix_len);
}
if(!client_id){
rc = MOSQ_ERR_NOMEM;
goto handle_connect_error;
}
context->assigned_id = true;
}
}
}
/* clientid_prefixes check */
if(db.config->clientid_prefixes){
if(strncmp(db.config->clientid_prefixes, client_id, strlen(db.config->clientid_prefixes))){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);
}
rc = MOSQ_ERR_AUTH;
goto handle_connect_error;
}
}
/* Check for an existing delayed auth check, reject if present */
HASH_FIND(hh_id, db.contexts_by_id_delayed_auth, client_id, strlen(client_id), found_context);
if(found_context){
rc = MOSQ_ERR_UNKNOWN;
goto handle_connect_error;
}
if(will){
rc = will__read(context, client_id, &will_struct, will_qos, will_retain);
if(rc){
if(context->protocol == mosq_p_mqtt5){
if(rc == MOSQ_ERR_DUPLICATE_PROPERTY || rc == MOSQ_ERR_PROTOCOL){
send__connack(context, 0, MQTT_RC_PROTOCOL_ERROR, NULL);
}else if(rc == MOSQ_ERR_MALFORMED_PACKET){
send__connack(context, 0, MQTT_RC_MALFORMED_PACKET, NULL);
}
}
goto handle_connect_error;
}
}else{
if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){
if(will_qos != 0 || will_retain != 0){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
}
}
if(username_flag){
rc = packet__read_string(&context->in_packet, &username, &slen);
if(rc == MOSQ_ERR_NOMEM){
rc = MOSQ_ERR_NOMEM;
goto handle_connect_error;
}else if(rc != MOSQ_ERR_SUCCESS){
if(context->protocol == mosq_p_mqtt31){
/* Username flag given, but no username. Ignore. */
username_flag = 0;
}else{
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
}
}else{
if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt31){
if(password_flag){
/* username_flag == 0 && password_flag == 1 is forbidden */
log__printf(NULL, MOSQ_LOG_ERR, "Protocol error from %s: password without username, closing connection.", client_id);
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
}
}
if(password_flag){
rc = packet__read_binary(&context->in_packet, (uint8_t **)&password, &slen);
if(rc == MOSQ_ERR_NOMEM){
rc = MOSQ_ERR_NOMEM;
goto handle_connect_error;
}else if(rc == MOSQ_ERR_MALFORMED_PACKET){
if(context->protocol == mosq_p_mqtt31){
/* Password flag given, but no password. Ignore. */
}else{
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
}
}
if(context->in_packet.pos != context->in_packet.remaining_length){
/* Surplus data at end of packet, this must be an error. */
if(protocol_version == PROTOCOL_VERSION_v5){
send__connack(context, 0, MQTT_RC_MALFORMED_PACKET, NULL);
}
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
/* Once context->id is set, if we return from this function with an error
* we must make sure that context->id is freed and set to NULL, so that the
* client isn't erroneously removed from the by_id hash table. */
context->id = client_id;
client_id = NULL;
#ifdef WITH_TLS
if(context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){
/* Don't need the username or password if provided */
mosquitto__FREE(username);
mosquitto__FREE(password);
if(!context->ssl){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL);
}
rc = MOSQ_ERR_AUTH;
goto handle_connect_error;
}
#ifdef FINAL_WITH_TLS_PSK
if(context->listener->psk_hint){
/* Client should have provided an identity to get this far. */
if(!context->username){
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL);
}
rc = MOSQ_ERR_AUTH;
goto handle_connect_error;
}
}else{
#endif /* FINAL_WITH_TLS_PSK */
rc = get_username_from_cert(context);
if(rc) goto handle_connect_error;
#ifdef FINAL_WITH_TLS_PSK
}
#endif /* FINAL_WITH_TLS_PSK */
}else
#endif /* WITH_TLS */
{
/* FIXME - these ensure the mosquitto_client_id() and
* mosquitto_client_username() functions work, but is hacky */
context->username = username;
context->password = password;
username = NULL; /* Avoid free() in error: below. */
password = NULL;
}
if(context->listener->use_username_as_clientid){
if(context->username){
mosquitto__FREE(context->id);
context->id = mosquitto__strdup(context->username);
if(!context->id){
rc = MOSQ_ERR_NOMEM;
goto handle_connect_error;
}
}else{
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);
}
rc = MOSQ_ERR_AUTH;
goto handle_connect_error;
}
}
context->clean_start = clean_start;
context->will = will_struct;
will_struct = NULL;
if(context->auth_method){
rc = mosquitto_security_auth_start(context, false, auth_data, auth_data_len, &auth_data_out, &auth_data_out_len);
mosquitto__FREE(auth_data);
if(rc == MOSQ_ERR_SUCCESS){
return connect__on_authorised(context, auth_data_out, auth_data_out_len);
}else if(rc == MOSQ_ERR_AUTH_CONTINUE){
mosquitto__set_state(context, mosq_cs_authenticating);
rc = send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len);
SAFE_FREE(auth_data_out);
return rc;
}else{
SAFE_FREE(auth_data_out);
will__clear(context);
if(rc == MOSQ_ERR_AUTH){
send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);
mosquitto__FREE(context->id);
goto handle_connect_error;
}else if(rc == MOSQ_ERR_NOT_SUPPORTED){
/* Client has requested extended authentication, but we don't support it. */
send__connack(context, 0, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL);
mosquitto__FREE(context->id);
goto handle_connect_error;
}else{
mosquitto__FREE(context->id);
goto handle_connect_error;
}
}
}else{
#ifdef WITH_TLS
if(context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){
/* Authentication assumed to be cleared */
}else
#endif
{
rc = mosquitto_basic_auth(context);
switch(rc){
case MOSQ_ERR_SUCCESS:
break;
case MOSQ_ERR_AUTH_DELAYED:
mosquitto__set_state(context, mosq_cs_delayed_auth);
HASH_ADD_KEYPTR(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context);
return MOSQ_ERR_SUCCESS;
break;
case MOSQ_ERR_AUTH:
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);
}
goto handle_connect_error;
break;
case MOSQ_ERR_UNSPECIFIED:
case MOSQ_ERR_IMPLEMENTATION_SPECIFIC:
case MOSQ_ERR_CLIENT_IDENTIFIER_NOT_VALID:
case MOSQ_ERR_BAD_USERNAME_OR_PASSWORD:
case MOSQ_ERR_SERVER_UNAVAILABLE:
case MOSQ_ERR_SERVER_BUSY:
case MOSQ_ERR_BANNED:
case MOSQ_ERR_BAD_AUTHENTICATION_METHOD:
case MOSQ_ERR_CONNECTION_RATE_EXCEEDED:
if(context->protocol == mosq_p_mqtt5){
send__connack(context, 0, (uint8_t)rc, NULL);
}else{
send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL);
}
goto handle_connect_error;
break;
default:
rc = MOSQ_ERR_UNKNOWN;
goto handle_connect_error;
break;
}
}
return connect__on_authorised(context, NULL, 0);
}
handle_connect_error:
mosquitto__FREE(auth_data);
mosquitto__FREE(client_id);
mosquitto__FREE(username);
mosquitto__FREE(password);
if(will_struct){
mosquitto_property_free_all(&will_struct->properties);
mosquitto__FREE(will_struct->msg.payload);
mosquitto__FREE(will_struct->msg.topic);
mosquitto__FREE(will_struct);
}
context->will = NULL;
/* We return an error here which means the client is freed later on. */
context->clean_start = true;
context->session_expiry_interval = 0;
context->will_delay_interval = 0;
return rc;
}