1 /*
2 Copyright (c) 2020 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 <openssl/bio.h>
22 #include <openssl/buffer.h>
23 #include <openssl/evp.h>
24 #include <openssl/rand.h>
25
26 #include "dynamic_security.h"
27 #include "mosquitto.h"
28 #include "mosquitto_broker.h"
29
30
31 /* ################################################################
32 * #
33 * # Base64 encoding/decoding
34 * #
35 * ################################################################ */
36
dynsec_auth__base64_encode(unsigned char * in,int in_len,char ** encoded)37 int dynsec_auth__base64_encode(unsigned char *in, int in_len, char **encoded)
38 {
39 BIO *bmem, *b64;
40 BUF_MEM *bptr = NULL;
41
42 if(in_len < 0) return 1;
43
44 b64 = BIO_new(BIO_f_base64());
45 if(b64 == NULL) return 1;
46
47 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
48 bmem = BIO_new(BIO_s_mem());
49 if(bmem == NULL){
50 BIO_free_all(b64);
51 return 1;
52 }
53 b64 = BIO_push(b64, bmem);
54 BIO_write(b64, in, in_len);
55 if(BIO_flush(b64) != 1){
56 BIO_free_all(b64);
57 return 1;
58 }
59 BIO_get_mem_ptr(b64, &bptr);
60 *encoded = mosquitto_malloc(bptr->length+1);
61 if(!(*encoded)){
62 BIO_free_all(b64);
63 return 1;
64 }
65 memcpy(*encoded, bptr->data, bptr->length);
66 (*encoded)[bptr->length] = '\0';
67 BIO_free_all(b64);
68
69 return 0;
70 }
71
72
dynsec_auth__base64_decode(char * in,unsigned char ** decoded,int * decoded_len)73 int dynsec_auth__base64_decode(char *in, unsigned char **decoded, int *decoded_len)
74 {
75 BIO *bmem, *b64;
76 size_t slen;
77
78 slen = strlen(in);
79
80 b64 = BIO_new(BIO_f_base64());
81 if(!b64){
82 return 1;
83 }
84 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
85
86 bmem = BIO_new(BIO_s_mem());
87 if(!bmem){
88 BIO_free_all(b64);
89 return 1;
90 }
91 b64 = BIO_push(b64, bmem);
92 BIO_write(bmem, in, (int)slen);
93
94 if(BIO_flush(bmem) != 1){
95 BIO_free_all(b64);
96 return 1;
97 }
98 *decoded = mosquitto_calloc(slen, 1);
99 if(!(*decoded)){
100 BIO_free_all(b64);
101 return 1;
102 }
103 *decoded_len = BIO_read(b64, *decoded, (int)slen);
104 BIO_free_all(b64);
105
106 if(*decoded_len <= 0){
107 mosquitto_free(*decoded);
108 *decoded = NULL;
109 *decoded_len = 0;
110 return 1;
111 }
112
113 return 0;
114 }
115
116
117 /* ################################################################
118 * #
119 * # Password functions
120 * #
121 * ################################################################ */
122
dynsec_auth__pw_hash(struct dynsec__client * client,const char * password,unsigned char * password_hash,int password_hash_len,bool new_password)123 int dynsec_auth__pw_hash(struct dynsec__client *client, const char *password, unsigned char *password_hash, int password_hash_len, bool new_password)
124 {
125 const EVP_MD *digest;
126 int iterations;
127
128 if(new_password){
129 if(RAND_bytes(client->pw.salt, sizeof(client->pw.salt)) != 1){
130 return MOSQ_ERR_UNKNOWN;
131 }
132 iterations = PW_DEFAULT_ITERATIONS;
133 }else{
134 iterations = client->pw.iterations;
135 }
136 if(iterations < 1){
137 return MOSQ_ERR_INVAL;
138 }
139 client->pw.iterations = iterations;
140
141 digest = EVP_get_digestbyname("sha512");
142 if(!digest){
143 return MOSQ_ERR_UNKNOWN;
144 }
145
146 return !PKCS5_PBKDF2_HMAC(password, (int)strlen(password),
147 client->pw.salt, sizeof(client->pw.salt), iterations,
148 digest, password_hash_len, password_hash);
149 }
150
151
152 /* ################################################################
153 * #
154 * # Username/password check
155 * #
156 * ################################################################ */
157
memcmp_const(const void * a,const void * b,size_t len)158 static int memcmp_const(const void *a, const void *b, size_t len)
159 {
160 size_t i;
161 int rc = 0;
162
163 if(!a || !b) return 1;
164
165 for(i=0; i<len; i++){
166 if( ((char *)a)[i] != ((char *)b)[i] ){
167 rc = 1;
168 }
169 }
170 return rc;
171 }
172
173
dynsec_auth__basic_auth_callback(int event,void * event_data,void * userdata)174 int dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata)
175 {
176 struct mosquitto_evt_basic_auth *ed = event_data;
177 struct dynsec__client *client;
178 unsigned char password_hash[64]; /* For SHA512 */
179 const char *clientid;
180
181 UNUSED(event);
182 UNUSED(userdata);
183
184 if(ed->username == NULL || ed->password == NULL) return MOSQ_ERR_PLUGIN_DEFER;
185
186 client = dynsec_clients__find(ed->username);
187 if(client){
188 if(client->disabled){
189 return MOSQ_ERR_AUTH;
190 }
191 if(client->clientid){
192 clientid = mosquitto_client_id(ed->client);
193 if(clientid == NULL || strcmp(client->clientid, clientid)){
194 return MOSQ_ERR_AUTH;
195 }
196 }
197 if(client->pw.valid && dynsec_auth__pw_hash(client, ed->password, password_hash, sizeof(password_hash), false) == MOSQ_ERR_SUCCESS){
198 if(memcmp_const(client->pw.password_hash, password_hash, sizeof(password_hash)) == 0){
199 return MOSQ_ERR_SUCCESS;
200 }else{
201 return MOSQ_ERR_AUTH;
202 }
203 }else{
204 return MOSQ_ERR_PLUGIN_DEFER;
205 }
206 }else{
207 return MOSQ_ERR_PLUGIN_DEFER;
208 }
209 }
210