1 /*
2  * WPA Supplicant / main() function for Win32 service
3  * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  *
8  * The root of wpa_supplicant configuration in registry is
9  * HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant. This level includes global
10  * parameters and a 'interfaces' subkey with all the interface configuration
11  * (adapter to confname mapping). Each such mapping is a subkey that has
12  * 'adapter' and 'config' values.
13  *
14  * This program can be run either as a normal command line application, e.g.,
15  * for debugging, with 'wpasvc.exe app' or as a Windows service. Service need
16  * to be registered with 'wpasvc.exe reg <full path to wpasvc.exe>'. After
17  * this, it can be started like any other Windows service (e.g., 'net start
18  * wpasvc') or it can be configured to start automatically through the Services
19  * tool in administrative tasks. The service can be unregistered with
20  * 'wpasvc.exe unreg'.
21  */
22 
23 #include "includes.h"
24 #include <windows.h>
25 
26 #include "common.h"
27 #include "wpa_supplicant_i.h"
28 #include "eloop.h"
29 
30 #ifndef WPASVC_NAME
31 #define WPASVC_NAME TEXT("wpasvc")
32 #endif
33 #ifndef WPASVC_DISPLAY_NAME
34 #define WPASVC_DISPLAY_NAME TEXT("wpa_supplicant service")
35 #endif
36 #ifndef WPASVC_DESCRIPTION
37 #define WPASVC_DESCRIPTION \
38 TEXT("Provides IEEE 802.1X and WPA/WPA2 supplicant functionality")
39 #endif
40 
41 static HANDLE kill_svc;
42 
43 static SERVICE_STATUS_HANDLE svc_status_handle;
44 static SERVICE_STATUS svc_status;
45 
46 
47 #ifndef WPA_KEY_ROOT
48 #define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
49 #endif
50 #ifndef WPA_KEY_PREFIX
51 #define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
52 #endif
53 
54 #ifdef UNICODE
55 #define TSTR "%S"
56 #else /* UNICODE */
57 #define TSTR "%s"
58 #endif /* UNICODE */
59 
60 
61 static int read_interface(struct wpa_global *global, HKEY _hk,
62 			  const TCHAR *name)
63 {
64 	HKEY hk;
65 #define TBUFLEN 255
66 	TCHAR adapter[TBUFLEN], config[TBUFLEN], ctrl_interface[TBUFLEN];
67 	DWORD buflen, val;
68 	LONG ret;
69 	struct wpa_interface iface;
70 	int skip_on_error = 0;
71 
72 	ret = RegOpenKeyEx(_hk, name, 0, KEY_QUERY_VALUE, &hk);
73 	if (ret != ERROR_SUCCESS) {
74 		printf("Could not open wpa_supplicant interface key\n");
75 		return -1;
76 	}
77 
78 	os_memset(&iface, 0, sizeof(iface));
79 	iface.driver = "ndis";
80 
81 	buflen = sizeof(ctrl_interface);
82 	ret = RegQueryValueEx(hk, TEXT("ctrl_interface"), NULL, NULL,
83 			      (LPBYTE) ctrl_interface, &buflen);
84 	if (ret == ERROR_SUCCESS) {
85 		ctrl_interface[TBUFLEN - 1] = TEXT('\0');
86 		wpa_unicode2ascii_inplace(ctrl_interface);
87 		printf("ctrl_interface[len=%d] '%s'\n",
88 		       (int) buflen, (char *) ctrl_interface);
89 		iface.ctrl_interface = (char *) ctrl_interface;
90 	}
91 
92 	buflen = sizeof(adapter);
93 	ret = RegQueryValueEx(hk, TEXT("adapter"), NULL, NULL,
94 			      (LPBYTE) adapter, &buflen);
95 	if (ret == ERROR_SUCCESS) {
96 		adapter[TBUFLEN - 1] = TEXT('\0');
97 		wpa_unicode2ascii_inplace(adapter);
98 		printf("adapter[len=%d] '%s'\n",
99 		       (int) buflen, (char *) adapter);
100 		iface.ifname = (char *) adapter;
101 	}
102 
103 	buflen = sizeof(config);
104 	ret = RegQueryValueEx(hk, TEXT("config"), NULL, NULL,
105 			      (LPBYTE) config, &buflen);
106 	if (ret == ERROR_SUCCESS) {
107 		config[sizeof(config) - 1] = '\0';
108 		wpa_unicode2ascii_inplace(config);
109 		printf("config[len=%d] '%s'\n",
110 		       (int) buflen, (char *) config);
111 		iface.confname = (char *) config;
112 	}
113 
114 	buflen = sizeof(val);
115 	ret = RegQueryValueEx(hk, TEXT("skip_on_error"), NULL, NULL,
116 			      (LPBYTE) &val, &buflen);
117 	if (ret == ERROR_SUCCESS && buflen == sizeof(val))
118 		skip_on_error = val;
119 
120 	RegCloseKey(hk);
121 
122 	if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) {
123 		if (skip_on_error)
124 			wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to "
125 				   "initialization failure", iface.ifname);
126 		else
127 			return -1;
128 	}
129 
130 	return 0;
131 }
132 
133 
134 static int wpa_supplicant_thread(void)
135 {
136 	int exitcode;
137 	struct wpa_params params;
138 	struct wpa_global *global;
139 	HKEY hk, ihk;
140 	DWORD val, buflen, i;
141 	LONG ret;
142 
143 	if (os_program_init())
144 		return -1;
145 
146 	os_memset(&params, 0, sizeof(params));
147 	params.wpa_debug_level = MSG_INFO;
148 
149 	ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX,
150 			   0, KEY_QUERY_VALUE, &hk);
151 	if (ret != ERROR_SUCCESS) {
152 		printf("Could not open wpa_supplicant registry key\n");
153 		return -1;
154 	}
155 
156 	buflen = sizeof(val);
157 	ret = RegQueryValueEx(hk, TEXT("debug_level"), NULL, NULL,
158 			      (LPBYTE) &val, &buflen);
159 	if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
160 		params.wpa_debug_level = val;
161 	}
162 
163 	buflen = sizeof(val);
164 	ret = RegQueryValueEx(hk, TEXT("debug_show_keys"), NULL, NULL,
165 			      (LPBYTE) &val, &buflen);
166 	if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
167 		params.wpa_debug_show_keys = val;
168 	}
169 
170 	buflen = sizeof(val);
171 	ret = RegQueryValueEx(hk, TEXT("debug_timestamp"), NULL, NULL,
172 			      (LPBYTE) &val, &buflen);
173 	if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
174 		params.wpa_debug_timestamp = val;
175 	}
176 
177 	buflen = sizeof(val);
178 	ret = RegQueryValueEx(hk, TEXT("debug_use_file"), NULL, NULL,
179 			      (LPBYTE) &val, &buflen);
180 	if (ret == ERROR_SUCCESS && buflen == sizeof(val) && val) {
181 		params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt";
182 	}
183 
184 	exitcode = 0;
185 	global = wpa_supplicant_init(&params);
186 	if (global == NULL) {
187 		printf("Failed to initialize wpa_supplicant\n");
188 		exitcode = -1;
189 	}
190 
191 	ret = RegOpenKeyEx(hk, TEXT("interfaces"), 0, KEY_ENUMERATE_SUB_KEYS,
192 			   &ihk);
193 	RegCloseKey(hk);
194 	if (ret != ERROR_SUCCESS) {
195 		printf("Could not open wpa_supplicant interfaces registry "
196 		       "key\n");
197 		return -1;
198 	}
199 
200 	for (i = 0; ; i++) {
201 		TCHAR name[255];
202 		DWORD namelen;
203 
204 		namelen = 255;
205 		ret = RegEnumKeyEx(ihk, i, name, &namelen, NULL, NULL, NULL,
206 				   NULL);
207 
208 		if (ret == ERROR_NO_MORE_ITEMS)
209 			break;
210 
211 		if (ret != ERROR_SUCCESS) {
212 			printf("RegEnumKeyEx failed: 0x%x\n",
213 			       (unsigned int) ret);
214 			break;
215 		}
216 
217 		if (namelen >= 255)
218 			namelen = 255 - 1;
219 		name[namelen] = '\0';
220 
221 		wpa_printf(MSG_DEBUG, "interface %d: %s\n", (int) i, name);
222 		if (read_interface(global, ihk, name) < 0)
223 			exitcode = -1;
224 	}
225 
226 	RegCloseKey(ihk);
227 
228 	if (exitcode == 0)
229 		exitcode = wpa_supplicant_run(global);
230 
231 	wpa_supplicant_deinit(global);
232 
233 	os_program_deinit();
234 
235 	return exitcode;
236 }
237 
238 
239 static DWORD svc_thread(LPDWORD param)
240 {
241 	int ret = wpa_supplicant_thread();
242 
243 	svc_status.dwCurrentState = SERVICE_STOPPED;
244 	svc_status.dwWaitHint = 0;
245 	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
246 		printf("SetServiceStatus() failed: %d\n",
247 		       (int) GetLastError());
248 	}
249 
250 	return ret;
251 }
252 
253 
254 static int register_service(const TCHAR *exe)
255 {
256 	SC_HANDLE svc, scm;
257 	SERVICE_DESCRIPTION sd;
258 
259 	printf("Registering service: " TSTR "\n", WPASVC_NAME);
260 
261 	scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
262 	if (!scm) {
263 		printf("OpenSCManager failed: %d\n", (int) GetLastError());
264 		return -1;
265 	}
266 
267 	svc = CreateService(scm, WPASVC_NAME, WPASVC_DISPLAY_NAME,
268 			    SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
269 			    SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
270 			    exe, NULL, NULL, NULL, NULL, NULL);
271 
272 	if (!svc) {
273 		printf("CreateService failed: %d\n\n", (int) GetLastError());
274 		CloseServiceHandle(scm);
275 		return -1;
276 	}
277 
278 	os_memset(&sd, 0, sizeof(sd));
279 	sd.lpDescription = WPASVC_DESCRIPTION;
280 	if (!ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd)) {
281 		printf("ChangeServiceConfig2 failed: %d\n",
282 		       (int) GetLastError());
283 		/* This is not a fatal error, so continue anyway. */
284 	}
285 
286 	CloseServiceHandle(svc);
287 	CloseServiceHandle(scm);
288 
289 	printf("Service registered successfully.\n");
290 
291 	return 0;
292 }
293 
294 
295 static int unregister_service(void)
296 {
297 	SC_HANDLE svc, scm;
298 	SERVICE_STATUS status;
299 
300 	printf("Unregistering service: " TSTR "\n", WPASVC_NAME);
301 
302 	scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
303 	if (!scm) {
304 		printf("OpenSCManager failed: %d\n", (int) GetLastError());
305 		return -1;
306 	}
307 
308 	svc = OpenService(scm, WPASVC_NAME, SERVICE_ALL_ACCESS | DELETE);
309 	if (!svc) {
310 		printf("OpenService failed: %d\n\n", (int) GetLastError());
311 		CloseServiceHandle(scm);
312 		return -1;
313 	}
314 
315 	if (QueryServiceStatus(svc, &status)) {
316 		if (status.dwCurrentState != SERVICE_STOPPED) {
317 			printf("Service currently active - stopping "
318 			       "service...\n");
319 			if (!ControlService(svc, SERVICE_CONTROL_STOP,
320 					    &status)) {
321 				printf("ControlService failed: %d\n",
322 				       (int) GetLastError());
323 			}
324 			Sleep(500);
325 		}
326 	}
327 
328 	if (DeleteService(svc)) {
329 		printf("Service unregistered successfully.\n");
330 	} else {
331 		printf("DeleteService failed: %d\n", (int) GetLastError());
332 	}
333 
334 	CloseServiceHandle(svc);
335 	CloseServiceHandle(scm);
336 
337 	return 0;
338 }
339 
340 
341 static void WINAPI service_ctrl_handler(DWORD control_code)
342 {
343 	switch (control_code) {
344 	case SERVICE_CONTROL_INTERROGATE:
345 		break;
346 	case SERVICE_CONTROL_SHUTDOWN:
347 	case SERVICE_CONTROL_STOP:
348 		svc_status.dwCurrentState = SERVICE_STOP_PENDING;
349 		svc_status.dwWaitHint = 2000;
350 		eloop_terminate();
351 		SetEvent(kill_svc);
352 		break;
353 	}
354 
355 	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
356 		printf("SetServiceStatus() failed: %d\n",
357 		       (int) GetLastError());
358 	}
359 }
360 
361 
362 static void WINAPI service_start(DWORD argc, LPTSTR *argv)
363 {
364 	DWORD id;
365 
366 	svc_status_handle = RegisterServiceCtrlHandler(WPASVC_NAME,
367 						       service_ctrl_handler);
368 	if (svc_status_handle == (SERVICE_STATUS_HANDLE) 0) {
369 		printf("RegisterServiceCtrlHandler failed: %d\n",
370 		       (int) GetLastError());
371 		return;
372 	}
373 
374 	os_memset(&svc_status, 0, sizeof(svc_status));
375 	svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
376 	svc_status.dwCurrentState = SERVICE_START_PENDING;
377 	svc_status.dwWaitHint = 1000;
378 
379 	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
380 		printf("SetServiceStatus() failed: %d\n",
381 		       (int) GetLastError());
382 		return;
383 	}
384 
385 	kill_svc = CreateEvent(0, TRUE, FALSE, 0);
386 	if (!kill_svc) {
387 		printf("CreateEvent failed: %d\n", (int) GetLastError());
388 		return;
389 	}
390 
391 	if (CreateThread(0, 0, (LPTHREAD_START_ROUTINE) svc_thread, 0, 0, &id)
392 	    == 0) {
393 		printf("CreateThread failed: %d\n", (int) GetLastError());
394 		return;
395 	}
396 
397 	if (svc_status.dwCurrentState == SERVICE_START_PENDING) {
398 		svc_status.dwCurrentState = SERVICE_RUNNING;
399 		svc_status.dwWaitHint = 0;
400 		svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
401 			SERVICE_ACCEPT_SHUTDOWN;
402 	}
403 
404 	if (!SetServiceStatus(svc_status_handle, &svc_status)) {
405 		printf("SetServiceStatus() failed: %d\n",
406 		       (int) GetLastError());
407 		return;
408 	}
409 
410 	/* wait until service gets killed */
411 	WaitForSingleObject(kill_svc, INFINITE);
412 }
413 
414 
415 int main(int argc, char *argv[])
416 {
417 	SERVICE_TABLE_ENTRY dt[] = {
418 		{ WPASVC_NAME, service_start },
419 		{ NULL, NULL }
420 	};
421 
422 	if (argc > 1) {
423 		if (os_strcmp(argv[1], "reg") == 0) {
424 			TCHAR *path;
425 			int ret;
426 
427 			if (argc < 3) {
428 				path = os_malloc(MAX_PATH * sizeof(TCHAR));
429 				if (path == NULL)
430 					return -1;
431 				if (!GetModuleFileName(NULL, path, MAX_PATH)) {
432 					printf("GetModuleFileName failed: "
433 					       "%d\n", (int) GetLastError());
434 					os_free(path);
435 					return -1;
436 				}
437 			} else {
438 				path = wpa_strdup_tchar(argv[2]);
439 				if (path == NULL)
440 					return -1;
441 			}
442 			ret = register_service(path);
443 			os_free(path);
444 			return ret;
445 		} else if (os_strcmp(argv[1], "unreg") == 0) {
446 			return unregister_service();
447 		} else if (os_strcmp(argv[1], "app") == 0) {
448 			return wpa_supplicant_thread();
449 		}
450 	}
451 
452 	if (!StartServiceCtrlDispatcher(dt)) {
453 		printf("StartServiceCtrlDispatcher failed: %d\n",
454 		       (int) GetLastError());
455 	}
456 
457 	return 0;
458 }
459