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