]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * WPA Supplicant / main() function for Win32 service | |
3 | * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | * |
8 | * The root of wpa_supplicant configuration in registry is | |
b39d1280 | 9 | * HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant. This level includes global |
6fc6879b JM |
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]; | |
f44b6b8c | 67 | DWORD buflen, val; |
6fc6879b JM |
68 | LONG ret; |
69 | struct wpa_interface iface; | |
f44b6b8c | 70 | int skip_on_error = 0; |
6fc6879b JM |
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 | ||
f44b6b8c JM |
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 | ||
6fc6879b JM |
120 | RegCloseKey(hk); |
121 | ||
1772d348 | 122 | if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) { |
f44b6b8c JM |
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 | } | |
6fc6879b JM |
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(¶ms, 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 | ||
99a10f8d JM |
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 | ||
6fc6879b JM |
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(¶ms); | |
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 | } |