1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/init.h>
10 #include <zephyr/net/wifi_credentials.h>
11 #include <sys/types.h>
12 
13 #include "wifi_credentials_internal.h"
14 
15 LOG_MODULE_REGISTER(wifi_credentials, CONFIG_WIFI_CREDENTIALS_LOG_LEVEL);
16 
17 static K_MUTEX_DEFINE(wifi_credentials_mutex);
18 
19 /* SSID cache: maps SSIDs to their storage indices */
20 static char ssid_cache[CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES][WIFI_SSID_MAX_LEN];
21 static size_t ssid_cache_lengths[CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES];
22 
23 /**
24  * @brief Finds index of given SSID if it exists.
25  *
26  * @param ssid		SSID to look for (buffer of WIFI_SSID_MAX_LEN length)
27  * @return		index if entry is found, -1 otherwise
28  */
lookup_idx(const uint8_t * ssid,size_t ssid_len)29 static inline ssize_t lookup_idx(const uint8_t *ssid, size_t ssid_len)
30 {
31 	for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
32 		if (ssid_len != ssid_cache_lengths[i]) {
33 			continue;
34 		}
35 
36 		if (strncmp(ssid, ssid_cache[i], ssid_len) == 0) {
37 			return i;
38 		}
39 	}
40 
41 	return -1;
42 }
43 
44 /**
45  * @brief Determine whether an index is currently used for storing network credentials.
46  *
47  * @param idx credential index
48  * @return true if index is used, false otherwise
49  */
is_entry_used(size_t idx)50 static inline bool is_entry_used(size_t idx)
51 {
52 	return ssid_cache_lengths[idx] != 0;
53 }
54 
55 /**
56  * @brief Finds unused index to store new entry at.
57  *
58  * @return		index if empty slot is found, -1 otherwise
59  */
lookup_unused_idx(void)60 static inline ssize_t lookup_unused_idx(void)
61 {
62 	for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
63 		if (!is_entry_used(i)) {
64 			return i;
65 		}
66 	}
67 
68 	return -1;
69 }
70 
init(void)71 static int init(void)
72 {
73 
74 	int ret;
75 
76 	k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
77 
78 	ret = wifi_credentials_backend_init();
79 	if (ret) {
80 		LOG_ERR("Initializing WiFi credentials storage backend failed, err: %d", ret);
81 	}
82 
83 	k_mutex_unlock(&wifi_credentials_mutex);
84 
85 	return 0;
86 }
87 
wifi_credentials_cache_ssid(size_t idx,const struct wifi_credentials_header * buf)88 void wifi_credentials_cache_ssid(size_t idx, const struct wifi_credentials_header *buf)
89 {
90 	memcpy(ssid_cache[idx], buf->ssid, buf->ssid_len);
91 	ssid_cache_lengths[idx] = buf->ssid_len;
92 }
93 
94 /**
95  * @brief Clear entry in SSID cache.
96  *
97  * @param idx credential index
98  */
wifi_credentials_uncache_ssid(size_t idx)99 void wifi_credentials_uncache_ssid(size_t idx)
100 {
101 	ssid_cache_lengths[idx] = 0;
102 }
103 
wifi_credentials_get_by_ssid_personal_struct(const char * ssid,size_t ssid_len,struct wifi_credentials_personal * buf)104 int wifi_credentials_get_by_ssid_personal_struct(const char *ssid, size_t ssid_len,
105 						 struct wifi_credentials_personal *buf)
106 {
107 	ssize_t idx;
108 	int ret;
109 
110 	if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
111 		LOG_ERR("Cannot %s WiFi credentials, %s", "retrieve", "SSID has invalid format");
112 		return -EINVAL;
113 	}
114 
115 	if (buf == NULL) {
116 		LOG_ERR("Cannot %s WiFi credentials, %s", "retrieve", "destination is NULL");
117 		return -EINVAL;
118 	}
119 
120 	k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
121 
122 	idx = lookup_idx(ssid, ssid_len);
123 	if (idx == -1) {
124 		LOG_DBG("Cannot %s WiFi credentials, %s", "retrieve", "SSID not found");
125 		ret = -ENOENT;
126 		goto exit;
127 	}
128 
129 	ret = wifi_credentials_load_entry(idx, buf, sizeof(struct wifi_credentials_personal));
130 	if (ret) {
131 		LOG_ERR("Failed to %s WiFi credentials at index %d, err: %d", "load", idx, ret);
132 		goto exit;
133 	}
134 
135 	if (buf->header.type != WIFI_SECURITY_TYPE_NONE &&
136 	    buf->header.type != WIFI_SECURITY_TYPE_PSK &&
137 	    buf->header.type != WIFI_SECURITY_TYPE_PSK_SHA256 &&
138 	    buf->header.type != WIFI_SECURITY_TYPE_SAE &&
139 	    buf->header.type != WIFI_SECURITY_TYPE_WPA_PSK) {
140 		LOG_ERR("Requested WiFi credentials entry is corrupted");
141 		ret = -EPROTO;
142 		goto exit;
143 	}
144 
145 exit:
146 	k_mutex_unlock(&wifi_credentials_mutex);
147 
148 	return ret;
149 }
150 
wifi_credentials_set_personal_struct(const struct wifi_credentials_personal * creds)151 int wifi_credentials_set_personal_struct(const struct wifi_credentials_personal *creds)
152 {
153 	ssize_t idx;
154 	int ret;
155 
156 	if (creds == NULL) {
157 		LOG_ERR("Cannot %s WiFi credentials, %s", "set", "credential not set");
158 		return -EINVAL;
159 	}
160 
161 	if (creds->header.ssid_len > WIFI_SSID_MAX_LEN || creds->header.ssid_len == 0) {
162 		LOG_ERR("Cannot %s WiFi credentials, %s", "set", "SSID has invalid format");
163 		return -EINVAL;
164 	}
165 
166 	k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
167 
168 	idx = lookup_idx(creds->header.ssid, creds->header.ssid_len);
169 	if (idx == -1) {
170 		idx = lookup_unused_idx();
171 		if (idx == -1) {
172 			LOG_ERR("Cannot %s WiFi credentials, %s", "store", "no space left");
173 			ret = -ENOBUFS;
174 			goto exit;
175 		}
176 	}
177 
178 	ret = wifi_credentials_store_entry(idx, creds, sizeof(struct wifi_credentials_personal));
179 	if (ret) {
180 		LOG_ERR("Failed to %s WiFi credentials at index %d, err: %d", "store", idx, ret);
181 		goto exit;
182 	}
183 
184 	wifi_credentials_cache_ssid(idx, &creds->header);
185 
186 exit:
187 	k_mutex_unlock(&wifi_credentials_mutex);
188 
189 	return ret;
190 }
191 
wifi_credentials_set_personal(const char * ssid,size_t ssid_len,enum wifi_security_type type,const uint8_t * bssid,size_t bssid_len,const char * password,size_t password_len,uint32_t flags,uint8_t channel,uint32_t timeout)192 int wifi_credentials_set_personal(const char *ssid, size_t ssid_len, enum wifi_security_type type,
193 				  const uint8_t *bssid, size_t bssid_len, const char *password,
194 				  size_t password_len, uint32_t flags, uint8_t channel,
195 				  uint32_t timeout)
196 {
197 	int ret = 0;
198 	uint8_t buf[ENTRY_MAX_LEN] = {0};
199 	struct wifi_credentials_header *header;
200 
201 	if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
202 		LOG_ERR("Cannot %s WiFi credentials, %s", "set", "SSID has invalid format");
203 		return -EINVAL;
204 	}
205 
206 	if (flags & WIFI_CREDENTIALS_FLAG_BSSID &&
207 	    (bssid_len != WIFI_MAC_ADDR_LEN || bssid == NULL)) {
208 		LOG_ERR("Cannot %s WiFi credentials, %s", "set",
209 			"provided flags indicated BSSID but no BSSID provided");
210 		return -EINVAL;
211 	}
212 
213 	if ((type != WIFI_SECURITY_TYPE_NONE && (password_len == 0 || password == NULL)) ||
214 	    (password_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN)) {
215 		LOG_ERR("Cannot %s WiFi credentials, %s", "set",
216 			"password not provided or invalid");
217 		return -EINVAL;
218 	}
219 
220 	/* pack entry */
221 	header = (struct wifi_credentials_header *)buf;
222 
223 	header->type = type;
224 	memcpy(header->ssid, ssid, ssid_len);
225 	header->ssid_len = ssid_len;
226 	header->flags = flags;
227 	header->channel = channel;
228 	header->timeout = timeout;
229 
230 	if (flags & WIFI_CREDENTIALS_FLAG_BSSID) {
231 		memcpy(header->bssid, bssid, WIFI_MAC_ADDR_LEN);
232 	}
233 
234 	switch (type) {
235 	case WIFI_SECURITY_TYPE_NONE:
236 		break;
237 	case WIFI_SECURITY_TYPE_PSK:
238 	case WIFI_SECURITY_TYPE_PSK_SHA256:
239 	case WIFI_SECURITY_TYPE_WPA_PSK:
240 	case WIFI_SECURITY_TYPE_SAE: {
241 		struct wifi_credentials_personal *header_personal =
242 			(struct wifi_credentials_personal *)buf;
243 
244 		memcpy(header_personal->password, password, password_len);
245 		header_personal->password_len = password_len;
246 		break;
247 	}
248 	default:
249 		LOG_ERR("Cannot %s WiFi credentials, provided security type %d is unsupported",
250 			"set", type);
251 		return -ENOTSUP;
252 	}
253 
254 	/* store entry */
255 	ret = wifi_credentials_set_personal_struct((struct wifi_credentials_personal *)buf);
256 
257 	return ret;
258 }
259 
wifi_credentials_get_by_ssid_personal(const char * ssid,size_t ssid_len,enum wifi_security_type * type,uint8_t * bssid_buf,size_t bssid_buf_len,char * password_buf,size_t password_buf_len,size_t * password_len,uint32_t * flags,uint8_t * channel,uint32_t * timeout)260 int wifi_credentials_get_by_ssid_personal(const char *ssid, size_t ssid_len,
261 					  enum wifi_security_type *type, uint8_t *bssid_buf,
262 					  size_t bssid_buf_len, char *password_buf,
263 					  size_t password_buf_len, size_t *password_len,
264 					  uint32_t *flags, uint8_t *channel, uint32_t *timeout)
265 {
266 	int ret = 0;
267 	uint8_t buf[ENTRY_MAX_LEN] = {0};
268 	struct wifi_credentials_header *header;
269 
270 	if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
271 		LOG_ERR("Cannot %s WiFi credentials, %s", "retrieve", "SSID has invalid format");
272 		return -EINVAL;
273 	}
274 
275 	if (bssid_buf_len != WIFI_MAC_ADDR_LEN || bssid_buf == NULL) {
276 		LOG_ERR("%s buffer needs to be provided", "BSSID");
277 		return -EINVAL;
278 	}
279 
280 	if (password_buf == NULL || password_buf_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN ||
281 	    password_buf_len == 0) {
282 		LOG_ERR("%s buffer needs to be provided", "WiFi password");
283 		return -EINVAL;
284 	}
285 
286 	/* load entry */
287 	ret = wifi_credentials_get_by_ssid_personal_struct(ssid, ssid_len,
288 							   (struct wifi_credentials_personal *)buf);
289 	if (ret) {
290 		return ret;
291 	}
292 
293 	/* unpack entry*/
294 	header = (struct wifi_credentials_header *)buf;
295 
296 	*type = header->type;
297 	*flags = header->flags;
298 	*channel = header->channel;
299 	*timeout = header->timeout;
300 
301 	if (header->flags & WIFI_CREDENTIALS_FLAG_BSSID) {
302 		memcpy(bssid_buf, header->bssid, WIFI_MAC_ADDR_LEN);
303 	}
304 
305 	switch (header->type) {
306 	case WIFI_SECURITY_TYPE_NONE:
307 		break;
308 	case WIFI_SECURITY_TYPE_PSK:
309 	case WIFI_SECURITY_TYPE_PSK_SHA256:
310 	case WIFI_SECURITY_TYPE_WPA_PSK:
311 	case WIFI_SECURITY_TYPE_SAE: {
312 		struct wifi_credentials_personal *header_personal =
313 			(struct wifi_credentials_personal *)buf;
314 
315 		memcpy(password_buf, header_personal->password, header_personal->password_len);
316 		*password_len = header_personal->password_len;
317 		break;
318 	}
319 	default:
320 		LOG_ERR("Cannot %s WiFi credentials, %s", "get",
321 			"the requested credentials have invalid WIFI_SECURITY_TYPE");
322 		ret = -EPROTO;
323 	}
324 	return ret;
325 }
326 
wifi_credentials_delete_by_ssid(const char * ssid,size_t ssid_len)327 int wifi_credentials_delete_by_ssid(const char *ssid, size_t ssid_len)
328 {
329 	ssize_t idx;
330 	int ret = 0;
331 
332 	if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
333 		LOG_ERR("Cannot %s WiFi credentials, %s", "delete", "SSID has invalid format");
334 		return -EINVAL;
335 	}
336 
337 	k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
338 
339 	idx = lookup_idx(ssid, ssid_len);
340 	if (idx == -1) {
341 		LOG_DBG("WiFi credentials entry was not found");
342 		goto exit;
343 	}
344 
345 	ret = wifi_credentials_delete_entry(idx);
346 	if (ret) {
347 		LOG_ERR("Failed to %s WiFi credentials index %d, err: %d", "delete", idx, ret);
348 		goto exit;
349 	}
350 
351 	wifi_credentials_uncache_ssid(idx);
352 
353 exit:
354 	k_mutex_unlock(&wifi_credentials_mutex);
355 	return ret;
356 }
357 
wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb,void * cb_arg)358 void wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb, void *cb_arg)
359 {
360 	k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
361 
362 	for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
363 		if (is_entry_used(i)) {
364 			cb(cb_arg, ssid_cache[i], ssid_cache_lengths[i]);
365 		}
366 	}
367 
368 	k_mutex_unlock(&wifi_credentials_mutex);
369 }
370 
wifi_credentials_is_empty(void)371 bool wifi_credentials_is_empty(void)
372 {
373 	k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
374 
375 	for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
376 		if (is_entry_used(i)) {
377 			k_mutex_unlock(&wifi_credentials_mutex);
378 			return false;
379 		}
380 	}
381 
382 	k_mutex_unlock(&wifi_credentials_mutex);
383 	return true;
384 }
385 
wifi_credentials_delete_all(void)386 int wifi_credentials_delete_all(void)
387 {
388 	int ret = 0;
389 
390 	k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
391 	for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
392 		if (is_entry_used(i)) {
393 			ret = wifi_credentials_delete_entry(i);
394 			if (ret) {
395 				LOG_ERR("Failed to %s WiFi credentials index %d, err: %d",
396 					"delete", i, ret);
397 				break;
398 			}
399 
400 			wifi_credentials_uncache_ssid(i);
401 		}
402 	}
403 
404 	k_mutex_unlock(&wifi_credentials_mutex);
405 	return ret;
406 }
407 
408 SYS_INIT(init, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY);
409