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 return; 235 } 236 237 238 VOID 239 WINAPI 240 W32TmServiceMain(DWORD argc, LPWSTR *argv) 241 { 242 int result; 243 DWORD dwInterval; 244 245 UNREFERENCED_PARAMETER(argc); 246 UNREFERENCED_PARAMETER(argv); 247 248 ServiceStatus.dwServiceType = SERVICE_WIN32; 249 ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 250 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 251 ServiceStatus.dwWin32ExitCode = 0; 252 ServiceStatus.dwServiceSpecificExitCode = 0; 253 ServiceStatus.dwCheckPoint = 0; 254 ServiceStatus.dwWaitHint = 0; 255 256 hStatus = RegisterServiceCtrlHandlerW(ServiceName, 257 ControlHandler); 258 if (!hStatus) 259 { 260 /* Registering Control Handler failed */ 261 return; 262 } 263 264 /* Create the stop event */ 265 hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 266 if (hStopEvent == NULL) 267 { 268 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 269 ServiceStatus.dwWin32ExitCode = GetLastError(); 270 ServiceStatus.dwControlsAccepted = 0; 271 SetServiceStatus(hStatus, &ServiceStatus); 272 return; 273 } 274 275 /* Get the interval */ 276 dwInterval = GetIntervalSetting(); 277 278 /* We report the running status to SCM. */ 279 ServiceStatus.dwCurrentState = SERVICE_RUNNING; 280 SetServiceStatus(hStatus, &ServiceStatus); 281 282 /* The worker loop of a service */ 283 for (;;) 284 { 285 result = SetTime(); 286 287 if (result) 288 DPRINT("W32Time Service failed to set clock.\n"); 289 else 290 DPRINT("W32Time Service successfully set clock.\n"); 291 292 if (result) 293 { 294 /* In general we do not want to stop this service for a single 295 * Internet read failure but there may be other reasons for which 296 * we really might want to stop it. 297 * Therefore this code is left here to make it easy to stop this 298 * service when the correct conditions can be determined, but it 299 * is left commented out. 300 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 301 ServiceStatus.dwWin32ExitCode = result; 302 SetServiceStatus(hStatus, &ServiceStatus); 303 return; 304 */ 305 } 306 307 if (WaitForSingleObject(hStopEvent, dwInterval * 1000) == WAIT_OBJECT_0) 308 { 309 CloseHandle(hStopEvent); 310 hStopEvent = NULL; 311 312 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 313 ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS; 314 ServiceStatus.dwControlsAccepted = 0; 315 SetServiceStatus(hStatus, &ServiceStatus); 316 DPRINT("Stopped W32Time Service\n"); 317 return; 318 } 319 } 320 return; 321 } 322 323 324 325 BOOL WINAPI 326 DllMain(HINSTANCE hinstDLL, 327 DWORD fdwReason, 328 LPVOID lpvReserved) 329 { 330 switch (fdwReason) 331 { 332 case DLL_PROCESS_ATTACH: 333 DisableThreadLibraryCalls(hinstDLL); 334 break; 335 336 case DLL_PROCESS_DETACH: 337 break; 338 } 339 340 return TRUE; 341 } 342 343 344 DWORD WINAPI 345 W32TimeSyncNow(LPCWSTR cmdline, 346 UINT blocking, 347 UINT flags) 348 { 349 DWORD result; 350 result = SetTime(); 351 if (result) 352 { 353 DPRINT("W32TimeSyncNow failed and clock not set.\n"); 354 } 355 else 356 { 357 DPRINT("W32TimeSyncNow succeeded and clock set.\n"); 358 } 359 return result; 360 } 361