/* * WPA Supplicant / main() function for Win32 service * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * * The root of wpa_supplicant configuration in registry is * HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant. This level includes global * parameters and a 'interfaces' subkey with all the interface configuration * (adapter to confname mapping). Each such mapping is a subkey that has * 'adapter' and 'config' values. * * This program can be run either as a normal command line application, e.g., * for debugging, with 'wpasvc.exe app' or as a Windows service. Service need * to be registered with 'wpasvc.exe reg <full path to wpasvc.exe>'. After * this, it can be started like any other Windows service (e.g., 'net start * wpasvc') or it can be configured to start automatically through the Services * tool in administrative tasks. The service can be unregistered with * 'wpasvc.exe unreg'. */ #include "includes.h" #include <windows.h> #include "common.h" #include "wpa_supplicant_i.h" #include "eloop.h" #ifndef WPASVC_NAME #define WPASVC_NAME TEXT("wpasvc") #endif #ifndef WPASVC_DISPLAY_NAME #define WPASVC_DISPLAY_NAME TEXT("wpa_supplicant service") #endif #ifndef WPASVC_DESCRIPTION #define WPASVC_DESCRIPTION \ TEXT("Provides IEEE 802.1X and WPA/WPA2 supplicant functionality") #endif static HANDLE kill_svc; static SERVICE_STATUS_HANDLE svc_status_handle; static SERVICE_STATUS svc_status; #ifndef WPA_KEY_ROOT #define WPA_KEY_ROOT HKEY_LOCAL_MACHINE #endif #ifndef WPA_KEY_PREFIX #define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") #endif #ifdef UNICODE #define TSTR "%S" #else /* UNICODE */ #define TSTR "%s" #endif /* UNICODE */ static int read_interface(struct wpa_global *global, HKEY _hk, const TCHAR *name) { HKEY hk; #define TBUFLEN 255 TCHAR adapter[TBUFLEN], config[TBUFLEN], ctrl_interface[TBUFLEN]; DWORD buflen; LONG ret; struct wpa_interface iface; ret = RegOpenKeyEx(_hk, name, 0, KEY_QUERY_VALUE, &hk); if (ret != ERROR_SUCCESS) { printf("Could not open wpa_supplicant interface key\n"); return -1; } os_memset(&iface, 0, sizeof(iface)); iface.driver = "ndis"; buflen = sizeof(ctrl_interface); ret = RegQueryValueEx(hk, TEXT("ctrl_interface"), NULL, NULL, (LPBYTE) ctrl_interface, &buflen); if (ret == ERROR_SUCCESS) { ctrl_interface[TBUFLEN - 1] = TEXT('\0'); wpa_unicode2ascii_inplace(ctrl_interface); printf("ctrl_interface[len=%d] '%s'\n", (int) buflen, (char *) ctrl_interface); iface.ctrl_interface = (char *) ctrl_interface; } buflen = sizeof(adapter); ret = RegQueryValueEx(hk, TEXT("adapter"), NULL, NULL, (LPBYTE) adapter, &buflen); if (ret == ERROR_SUCCESS) { adapter[TBUFLEN - 1] = TEXT('\0'); wpa_unicode2ascii_inplace(adapter); printf("adapter[len=%d] '%s'\n", (int) buflen, (char *) adapter); iface.ifname = (char *) adapter; } buflen = sizeof(config); ret = RegQueryValueEx(hk, TEXT("config"), NULL, NULL, (LPBYTE) config, &buflen); if (ret == ERROR_SUCCESS) { config[sizeof(config) - 1] = '\0'; wpa_unicode2ascii_inplace(config); printf("config[len=%d] '%s'\n", (int) buflen, (char *) config); iface.confname = (char *) config; } RegCloseKey(hk); if (wpa_supplicant_add_iface(global, &iface) == NULL) return -1; return 0; } static int wpa_supplicant_thread(void) { int exitcode; struct wpa_params params; struct wpa_global *global; HKEY hk, ihk; DWORD val, buflen, i; LONG ret; if (os_program_init()) return -1; os_memset(¶ms, 0, sizeof(params)); params.wpa_debug_level = MSG_INFO; ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX, 0, KEY_QUERY_VALUE, &hk); if (ret != ERROR_SUCCESS) { printf("Could not open wpa_supplicant registry key\n"); return -1; } buflen = sizeof(val); ret = RegQueryValueEx(hk, TEXT("debug_level"), NULL, NULL, (LPBYTE) &val, &buflen); if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { params.wpa_debug_level = val; } buflen = sizeof(val); ret = RegQueryValueEx(hk, TEXT("debug_show_keys"), NULL, NULL, (LPBYTE) &val, &buflen); if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { params.wpa_debug_show_keys = val; } buflen = sizeof(val); ret = RegQueryValueEx(hk, TEXT("debug_use_file"), NULL, NULL, (LPBYTE) &val, &buflen); if (ret == ERROR_SUCCESS && buflen == sizeof(val) && val) { params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt"; } exitcode = 0; global = wpa_supplicant_init(¶ms); if (global == NULL) { printf("Failed to initialize wpa_supplicant\n"); exitcode = -1; } ret = RegOpenKeyEx(hk, TEXT("interfaces"), 0, KEY_ENUMERATE_SUB_KEYS, &ihk); RegCloseKey(hk); if (ret != ERROR_SUCCESS) { printf("Could not open wpa_supplicant interfaces registry " "key\n"); return -1; } for (i = 0; ; i++) { TCHAR name[255]; DWORD namelen; namelen = 255; ret = RegEnumKeyEx(ihk, i, name, &namelen, NULL, NULL, NULL, NULL); if (ret == ERROR_NO_MORE_ITEMS) break; if (ret != ERROR_SUCCESS) { printf("RegEnumKeyEx failed: 0x%x\n", (unsigned int) ret); break; } if (namelen >= 255) namelen = 255 - 1; name[namelen] = '\0'; wpa_printf(MSG_DEBUG, "interface %d: %s\n", (int) i, name); if (read_interface(global, ihk, name) < 0) exitcode = -1; } RegCloseKey(ihk); if (exitcode == 0) exitcode = wpa_supplicant_run(global); wpa_supplicant_deinit(global); os_program_deinit(); return exitcode; } static DWORD svc_thread(LPDWORD param) { int ret = wpa_supplicant_thread(); svc_status.dwCurrentState = SERVICE_STOPPED; svc_status.dwWaitHint = 0; if (!SetServiceStatus(svc_status_handle, &svc_status)) { printf("SetServiceStatus() failed: %d\n", (int) GetLastError()); } return ret; } static int register_service(const TCHAR *exe) { SC_HANDLE svc, scm; SERVICE_DESCRIPTION sd; printf("Registering service: " TSTR "\n", WPASVC_NAME); scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); if (!scm) { printf("OpenSCManager failed: %d\n", (int) GetLastError()); return -1; } svc = CreateService(scm, WPASVC_NAME, WPASVC_DISPLAY_NAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, exe, NULL, NULL, NULL, NULL, NULL); if (!svc) { printf("CreateService failed: %d\n\n", (int) GetLastError()); CloseServiceHandle(scm); return -1; } os_memset(&sd, 0, sizeof(sd)); sd.lpDescription = WPASVC_DESCRIPTION; if (!ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd)) { printf("ChangeServiceConfig2 failed: %d\n", (int) GetLastError()); /* This is not a fatal error, so continue anyway. */ } CloseServiceHandle(svc); CloseServiceHandle(scm); printf("Service registered successfully.\n"); return 0; } static int unregister_service(void) { SC_HANDLE svc, scm; SERVICE_STATUS status; printf("Unregistering service: " TSTR "\n", WPASVC_NAME); scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); if (!scm) { printf("OpenSCManager failed: %d\n", (int) GetLastError()); return -1; } svc = OpenService(scm, WPASVC_NAME, SERVICE_ALL_ACCESS | DELETE); if (!svc) { printf("OpenService failed: %d\n\n", (int) GetLastError()); CloseServiceHandle(scm); return -1; } if (QueryServiceStatus(svc, &status)) { if (status.dwCurrentState != SERVICE_STOPPED) { printf("Service currently active - stopping " "service...\n"); if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) { printf("ControlService failed: %d\n", (int) GetLastError()); } Sleep(500); } } if (DeleteService(svc)) { printf("Service unregistered successfully.\n"); } else { printf("DeleteService failed: %d\n", (int) GetLastError()); } CloseServiceHandle(svc); CloseServiceHandle(scm); return 0; } static void WINAPI service_ctrl_handler(DWORD control_code) { switch (control_code) { case SERVICE_CONTROL_INTERROGATE: break; case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: svc_status.dwCurrentState = SERVICE_STOP_PENDING; svc_status.dwWaitHint = 2000; eloop_terminate(); SetEvent(kill_svc); break; } if (!SetServiceStatus(svc_status_handle, &svc_status)) { printf("SetServiceStatus() failed: %d\n", (int) GetLastError()); } } static void WINAPI service_start(DWORD argc, LPTSTR *argv) { DWORD id; svc_status_handle = RegisterServiceCtrlHandler(WPASVC_NAME, service_ctrl_handler); if (svc_status_handle == (SERVICE_STATUS_HANDLE) 0) { printf("RegisterServiceCtrlHandler failed: %d\n", (int) GetLastError()); return; } os_memset(&svc_status, 0, sizeof(svc_status)); svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; svc_status.dwCurrentState = SERVICE_START_PENDING; svc_status.dwWaitHint = 1000; if (!SetServiceStatus(svc_status_handle, &svc_status)) { printf("SetServiceStatus() failed: %d\n", (int) GetLastError()); return; } kill_svc = CreateEvent(0, TRUE, FALSE, 0); if (!kill_svc) { printf("CreateEvent failed: %d\n", (int) GetLastError()); return; } if (CreateThread(0, 0, (LPTHREAD_START_ROUTINE) svc_thread, 0, 0, &id) == 0) { printf("CreateThread failed: %d\n", (int) GetLastError()); return; } if (svc_status.dwCurrentState == SERVICE_START_PENDING) { svc_status.dwCurrentState = SERVICE_RUNNING; svc_status.dwWaitHint = 0; svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; } if (!SetServiceStatus(svc_status_handle, &svc_status)) { printf("SetServiceStatus() failed: %d\n", (int) GetLastError()); return; } /* wait until service gets killed */ WaitForSingleObject(kill_svc, INFINITE); } int main(int argc, char *argv[]) { SERVICE_TABLE_ENTRY dt[] = { { WPASVC_NAME, service_start }, { NULL, NULL } }; if (argc > 1) { if (os_strcmp(argv[1], "reg") == 0) { TCHAR *path; int ret; if (argc < 3) { path = os_malloc(MAX_PATH * sizeof(TCHAR)); if (path == NULL) return -1; if (!GetModuleFileName(NULL, path, MAX_PATH)) { printf("GetModuleFileName failed: " "%d\n", (int) GetLastError()); os_free(path); return -1; } } else { path = wpa_strdup_tchar(argv[2]); if (path == NULL) return -1; } ret = register_service(path); os_free(path); return ret; } else if (os_strcmp(argv[1], "unreg") == 0) { return unregister_service(); } else if (os_strcmp(argv[1], "app") == 0) { return wpa_supplicant_thread(); } } if (!StartServiceCtrlDispatcher(dt)) { printf("StartServiceCtrlDispatcher failed: %d\n", (int) GetLastError()); } return 0; }