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