1 /* 2 * PROJECT: ReactOS W32Time Service 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Create W32Time Service that reqularly syncs clock to Internet Time 5 * COPYRIGHT: Copyright 2006 Ged Murphy <gedmurphy@gmail.com> 6 * Copyright 2018 Doug Lyons 7 */ 8 9 #include "w32time.h" 10 #include <debug.h> 11 #include <strsafe.h> 12 13 SERVICE_STATUS ServiceStatus; 14 SERVICE_STATUS_HANDLE hStatus; 15 HANDLE hStopEvent = NULL; 16 static WCHAR ServiceName[] = L"W32Time"; 17 18 int InitService(VOID); 19 20 BOOL 21 SystemSetTime(LPSYSTEMTIME lpSystemTime) 22 { 23 HANDLE hToken; 24 DWORD PrevSize; 25 TOKEN_PRIVILEGES priv, previouspriv; 26 BOOL Ret = FALSE; 27 28 /* 29 * Enable the SeSystemtimePrivilege privilege 30 */ 31 32 if (OpenProcessToken(GetCurrentProcess(), 33 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 34 &hToken)) 35 { 36 priv.PrivilegeCount = 1; 37 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 38 39 if (LookupPrivilegeValueW(NULL, 40 SE_SYSTEMTIME_NAME, 41 &priv.Privileges[0].Luid)) 42 { 43 if (AdjustTokenPrivileges(hToken, 44 FALSE, 45 &priv, 46 sizeof(previouspriv), 47 &previouspriv, 48 &PrevSize) && 49 GetLastError() == ERROR_SUCCESS) 50 { 51 /* 52 * We successfully enabled it, we're permitted to change the time. 53 */ 54 Ret = SetSystemTime(lpSystemTime); 55 56 /* 57 * For the sake of security, restore the previous status again 58 */ 59 if (previouspriv.PrivilegeCount > 0) 60 { 61 AdjustTokenPrivileges(hToken, 62 FALSE, 63 &previouspriv, 64 0, 65 NULL, 66 0); 67 } 68 } 69 } 70 CloseHandle(hToken); 71 } 72 73 return Ret; 74 } 75 76 77 /* 78 * NTP servers state the number of seconds passed since 79 * 1st Jan, 1900. The time returned from the server 80 * needs adding to that date to get the current Gregorian time 81 */ 82 static DWORD 83 UpdateSystemTime(ULONG ulTime) 84 { 85 FILETIME ftNew; 86 LARGE_INTEGER li; 87 SYSTEMTIME stNew; 88 89 /* Time at 1st Jan 1900 */ 90 stNew.wYear = 1900; 91 stNew.wMonth = 1; 92 stNew.wDay = 1; 93 stNew.wHour = 0; 94 stNew.wMinute = 0; 95 stNew.wSecond = 0; 96 stNew.wMilliseconds = 0; 97 98 /* Convert to a file time */ 99 if (!SystemTimeToFileTime(&stNew, &ftNew)) 100 { 101 return GetLastError(); 102 } 103 104 /* Add on the time passed since 1st Jan 1900 */ 105 li = *(LARGE_INTEGER *)&ftNew; 106 li.QuadPart += (LONGLONG)10000000 * ulTime; 107 ftNew = * (FILETIME *)&li; 108 109 /* Convert back to a system time */ 110 if (!FileTimeToSystemTime(&ftNew, &stNew)) 111 { 112 return GetLastError(); 113 } 114 115 if (!SystemSetTime(&stNew)) 116 { 117 return GetLastError(); 118 } 119 120 return ERROR_SUCCESS; 121 } 122 123 124 static DWORD 125 GetIntervalSetting(VOID) 126 { 127 HKEY hKey; 128 DWORD dwData; 129 DWORD dwSize = sizeof(dwData); 130 LONG lRet; 131 132 dwData = 0; 133 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 134 L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\TimeProviders\\NtpClient", 135 0, 136 KEY_QUERY_VALUE, 137 &hKey); 138 if (lRet == ERROR_SUCCESS) 139 { 140 /* 141 * This key holds the update interval in seconds. 142 * It is useful for testing to set it to a value of 10 (Decimal). 143 * This will cause the clock to try and update every 10 seconds. 144 * So you can change the time and expect it to be set back correctly in 10-20 seconds. 145 */ 146 lRet = RegQueryValueExW(hKey, 147 L"SpecialPollInterval", 148 NULL, 149 NULL, 150 (LPBYTE)&dwData, 151 &dwSize); 152 RegCloseKey(hKey); 153 } 154 155 if (lRet != ERROR_SUCCESS || dwData < 120) 156 return 9 * 60 * 60; // 9 hours, because Windows uses 9 hrs, 6 mins and 8 seconds by default. 157 else 158 return dwData; 159 } 160 161 162 DWORD 163 SetTime(VOID) 164 { 165 ULONG ulTime; 166 LONG lRet; 167 HKEY hKey; 168 WCHAR szData[MAX_VALUE_NAME] = L""; 169 DWORD cbName = sizeof(szData); 170 171 DPRINT("Entered SetTime.\n"); 172 173 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 174 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers", 175 0, 176 KEY_QUERY_VALUE, 177 &hKey); 178 if (lRet != ERROR_SUCCESS) 179 { 180 return lRet; 181 } 182 183 lRet = RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)szData, &cbName); 184 if (lRet == ERROR_SUCCESS) 185 { 186 cbName = sizeof(szData); 187 lRet = RegQueryValueExW(hKey, szData, NULL, NULL, (LPBYTE)szData, &cbName); 188 } 189 190 RegCloseKey(hKey); 191 192 DPRINT("Time Server is '%S'.\n", szData); 193 194 /* Is the given string empty? */ 195 if (cbName == 0 || szData[0] == '\0') 196 { 197 DPRINT("The time NTP server couldn't be found, the string is empty!\n"); 198 return ERROR_INVALID_DATA; 199 } 200 201 ulTime = GetServerTime(szData); 202 203 if (ulTime != 0) 204 { 205 return UpdateSystemTime(ulTime); 206 } 207 else 208 return ERROR_GEN_FAILURE; 209 } 210 211 212 /* Control handler function */ 213 VOID WINAPI 214 ControlHandler(DWORD request) 215 { 216 switch (request) 217 { 218 case SERVICE_CONTROL_STOP: 219 case SERVICE_CONTROL_SHUTDOWN: 220 DPRINT("Stopping W32Time Service\n"); 221 222 ServiceStatus.dwWin32ExitCode = 0; 223 ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; 224 SetServiceStatus(hStatus, &ServiceStatus); 225 226 if (hStopEvent) 227 SetEvent(hStopEvent); 228 return; 229 230 default: 231 break; 232 } 233 } 234 235 236 VOID 237 WINAPI 238 W32TmServiceMain(DWORD argc, LPWSTR *argv) 239 { 240 LONG error; 241 DWORD dwInterval; 242 HKEY hKey; 243 WCHAR szData[8]; 244 DWORD cbData; 245 BOOL bNoSync; 246 247 UNREFERENCED_PARAMETER(argc); 248 UNREFERENCED_PARAMETER(argv); 249 250 ServiceStatus.dwServiceType = SERVICE_WIN32; 251 ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 252 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 253 ServiceStatus.dwWin32ExitCode = 0; 254 ServiceStatus.dwServiceSpecificExitCode = 0; 255 ServiceStatus.dwCheckPoint = 0; 256 ServiceStatus.dwWaitHint = 0; 257 258 hStatus = RegisterServiceCtrlHandlerW(ServiceName, 259 ControlHandler); 260 if (!hStatus) 261 { 262 /* Registering Control Handler failed */ 263 return; 264 } 265 266 /* Create the stop event */ 267 hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 268 if (hStopEvent == NULL) 269 { 270 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 271 ServiceStatus.dwWin32ExitCode = GetLastError(); 272 ServiceStatus.dwControlsAccepted = 0; 273 SetServiceStatus(hStatus, &ServiceStatus); 274 return; 275 } 276 277 /* Get the interval */ 278 dwInterval = GetIntervalSetting(); 279 280 /* We report the running status to SCM. */ 281 ServiceStatus.dwCurrentState = SERVICE_RUNNING; 282 SetServiceStatus(hStatus, &ServiceStatus); 283 284 /* The service's worker loop */ 285 for (;;) 286 { 287 /* The default is NoSync */ 288 bNoSync = TRUE; 289 290 /* TODO: Use RegNotifyChangeKeyValue() when implemented */ 291 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, 292 L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\Parameters", 293 0, 294 KEY_QUERY_VALUE, 295 &hKey) == ERROR_SUCCESS) 296 { 297 cbData = sizeof(szData); 298 RegQueryValueExW(hKey, L"Type", NULL, NULL, (LPBYTE)szData, &cbData); 299 szData[ARRAYSIZE(szData) - 1] = UNICODE_NULL; /* Avoid buffer overrun */ 300 bNoSync = (_wcsicmp(szData, L"NoSync") == 0); 301 RegCloseKey(hKey); 302 } 303 304 if (!bNoSync) 305 { 306 error = SetTime(); 307 if (error != ERROR_SUCCESS) 308 { 309 DPRINT("W32Time Service failed to set clock: 0x%08lX\n", error); 310 #if 0 311 /* 312 * In general, we do not want to stop this service for a single 313 * Internet read failure but there may be other reasons for which 314 * we really might want to stop it. Therefore this code is left here. 315 */ 316 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 317 ServiceStatus.dwWin32ExitCode = error; 318 SetServiceStatus(hStatus, &ServiceStatus); 319 return; 320 #endif 321 } 322 } 323 324 if (WaitForSingleObject(hStopEvent, dwInterval * 1000) == WAIT_OBJECT_0) 325 { 326 CloseHandle(hStopEvent); 327 hStopEvent = NULL; 328 329 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 330 ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS; 331 ServiceStatus.dwControlsAccepted = 0; 332 SetServiceStatus(hStatus, &ServiceStatus); 333 DPRINT("Stopped W32Time Service\n"); 334 return; 335 } 336 } 337 } 338 339 340 BOOL WINAPI 341 DllMain(HINSTANCE hinstDLL, 342 DWORD fdwReason, 343 LPVOID lpvReserved) 344 { 345 switch (fdwReason) 346 { 347 case DLL_PROCESS_ATTACH: 348 DisableThreadLibraryCalls(hinstDLL); 349 break; 350 351 case DLL_PROCESS_DETACH: 352 break; 353 } 354 355 return TRUE; 356 } 357 358 359 DWORD WINAPI 360 W32TimeSyncNow(LPCWSTR cmdline, 361 UINT blocking, 362 UINT flags) 363 { 364 DWORD result; 365 result = SetTime(); 366 if (result) 367 { 368 DPRINT("W32TimeSyncNow failed and clock not set.\n"); 369 } 370 else 371 { 372 DPRINT("W32TimeSyncNow succeeded and clock set.\n"); 373 } 374 return result; 375 } 376