1/*
2 * UFD routines for Wi-Fi Protected Setup
3 * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10#include "common.h"
11#include <sys/types.h>
12#include <sys/stat.h>
13#include <fcntl.h>
14#include <dirent.h>
15
16#include "wps/wps.h"
17#include "wps/wps_i.h"
18
19#ifdef CONFIG_NATIVE_WINDOWS
20#define UFD_DIR1 "%s\\SMRTNTKY"
21#define UFD_DIR2 UFD_DIR1 "\\WFAWSC"
22#define UFD_FILE UFD_DIR2 "\\%s"
23#else /* CONFIG_NATIVE_WINDOWS */
24#define UFD_DIR1 "%s/SMRTNTKY"
25#define UFD_DIR2 UFD_DIR1 "/WFAWSC"
26#define UFD_FILE UFD_DIR2 "/%s"
27#endif /* CONFIG_NATIVE_WINDOWS */
28
29
30struct wps_ufd_data {
31	int ufd_fd;
32};
33
34
35static int dev_pwd_e_file_filter(const struct dirent *entry)
36{
37	unsigned int prefix;
38	char ext[5];
39
40	if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2)
41		return 0;
42	if (prefix == 0)
43		return 0;
44	if (os_strcasecmp(ext, "WFA") != 0)
45		return 0;
46
47	return 1;
48}
49
50
51static int wps_get_dev_pwd_e_file_name(char *path, char *file_name)
52{
53	struct dirent **namelist;
54	int i, file_num;
55
56	file_num = scandir(path, &namelist, &dev_pwd_e_file_filter,
57			   alphasort);
58	if (file_num < 0) {
59		wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)",
60			   errno, strerror(errno));
61		return -1;
62	}
63	if (file_num == 0) {
64		wpa_printf(MSG_ERROR, "WPS: OOB file not found");
65		os_free(namelist);
66		return -1;
67	}
68	os_strlcpy(file_name, namelist[0]->d_name, 13);
69	for (i = 0; i < file_num; i++)
70		os_free(namelist[i]);
71	os_free(namelist);
72	return 0;
73}
74
75
76static int get_file_name(struct wps_context *wps, int registrar,
77			 const char *path, char *file_name)
78{
79	switch (wps->oob_conf.oob_method) {
80	case OOB_METHOD_CRED:
81		os_snprintf(file_name, 13, "00000000.WSC");
82		break;
83	case OOB_METHOD_DEV_PWD_E:
84		if (registrar) {
85			char temp[128];
86			os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
87			if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0)
88				return -1;
89		} else {
90			u8 *mac_addr = wps->dev.mac_addr;
91
92			os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA",
93				    mac_addr[2], mac_addr[3], mac_addr[4],
94				    mac_addr[5]);
95		}
96		break;
97	case OOB_METHOD_DEV_PWD_R:
98		os_snprintf(file_name, 13, "00000000.WFA");
99		break;
100	default:
101		wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method");
102		return -1;
103	}
104	return 0;
105}
106
107
108static int ufd_mkdir(const char *path)
109{
110	if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) {
111		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory "
112			   "'%s': %d (%s)", path, errno, strerror(errno));
113		return -1;
114	}
115	return 0;
116}
117
118
119static void * init_ufd(struct wps_context *wps,
120		       struct oob_device_data *oob_dev, int registrar)
121{
122	int write_f;
123	char temp[128];
124	char *path = oob_dev->device_path;
125	char filename[13];
126	struct wps_ufd_data *data;
127	int ufd_fd;
128
129	if (path == NULL)
130		return NULL;
131
132	write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ?
133		!registrar : registrar;
134
135	if (get_file_name(wps, registrar, path, filename) < 0) {
136		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name");
137		return NULL;
138	}
139
140	if (write_f) {
141		os_snprintf(temp, sizeof(temp), UFD_DIR1, path);
142		if (ufd_mkdir(temp))
143			return NULL;
144		os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
145		if (ufd_mkdir(temp))
146			return NULL;
147	}
148
149	os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename);
150	if (write_f)
151		ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC,
152			      S_IRUSR | S_IWUSR);
153	else
154		ufd_fd = open(temp, O_RDONLY);
155	if (ufd_fd < 0) {
156		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s",
157			   temp, strerror(errno));
158		return NULL;
159	}
160
161	data = os_zalloc(sizeof(*data));
162	if (data == NULL) {
163		close(ufd_fd);
164		return NULL;
165	}
166	data->ufd_fd = ufd_fd;
167	return data;
168}
169
170
171static struct wpabuf * read_ufd(void *priv)
172{
173	struct wps_ufd_data *data = priv;
174	struct wpabuf *buf;
175	struct stat s;
176	size_t file_size;
177
178	if (fstat(data->ufd_fd, &s) < 0) {
179		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size");
180		return NULL;
181	}
182
183	file_size = s.st_size;
184	buf = wpabuf_alloc(file_size);
185	if (buf == NULL) {
186		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read "
187			   "buffer");
188		return NULL;
189	}
190
191	if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) !=
192	    (int) file_size) {
193		wpabuf_free(buf);
194		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read");
195		return NULL;
196	}
197	wpabuf_put(buf, file_size);
198	return buf;
199}
200
201
202static int write_ufd(void *priv, struct wpabuf *buf)
203{
204	struct wps_ufd_data *data = priv;
205
206	if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) !=
207	    (int) wpabuf_len(buf)) {
208		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write");
209		return -1;
210	}
211	return 0;
212}
213
214
215static void deinit_ufd(void *priv)
216{
217	struct wps_ufd_data *data = priv;
218	close(data->ufd_fd);
219	os_free(data);
220}
221
222
223struct oob_device_data oob_ufd_device_data = {
224	.device_name	= NULL,
225	.device_path	= NULL,
226	.init_func	= init_ufd,
227	.read_func	= read_ufd,
228	.write_func	= write_ufd,
229	.deinit_func	= deinit_ufd,
230};
231