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 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, 133 L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\TimeProviders\\NtpClient", 134 0, 135 KEY_QUERY_VALUE, 136 &hKey) == ERROR_SUCCESS) 137 { 138 /* This key holds the update interval in seconds 139 * It is useful for testing to set it to a value of 10 (Decimal) 140 * This will cause the clock to try and update every 10 seconds 141 * So you can change the time and expect it to be set back correctly in 10-20 seconds 142 */ 143 lRet = RegQueryValueExW(hKey, 144 L"SpecialPollInterval", 145 NULL, 146 NULL, 147 (LPBYTE)&dwData, 148 &dwSize); 149 RegCloseKey(hKey); 150 } 151 152 if (lRet != ERROR_SUCCESS) 153 return 0; 154 else 155 return dwData; 156 } 157 158 159 DWORD 160 SetTime(VOID) 161 { 162 ULONG ulTime; 163 LONG lRet; 164 HKEY hKey; 165 WCHAR szData[MAX_VALUE_NAME] = L""; 166 DWORD cbName = sizeof(szData); 167 168 DPRINT("Entered SetTime.\n"); 169 170 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 171 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers", 172 0, 173 KEY_QUERY_VALUE, 174 &hKey); 175 if (lRet != ERROR_SUCCESS) 176 { 177 return lRet; 178 } 179 180 lRet = RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)szData, &cbName); 181 if (lRet == ERROR_SUCCESS) 182 { 183 cbName = sizeof(szData); 184 lRet = RegQueryValueExW(hKey, szData, NULL, NULL, (LPBYTE)szData, &cbName); 185 } 186 187 RegCloseKey(hKey); 188 189 DPRINT("Time Server is '%S'.\n", szData); 190 191 ulTime = GetServerTime(szData); 192 193 if (ulTime != 0) 194 { 195 return UpdateSystemTime(ulTime); 196 } 197 else 198 return ERROR_GEN_FAILURE; 199 } 200 201 202 /* Control handler function */ 203 VOID WINAPI 204 ControlHandler(DWORD request) 205 { 206 switch (request) 207 { 208 case SERVICE_CONTROL_STOP: 209 DPRINT("W32Time Service stopped.\n"); 210 211 ServiceStatus.dwWin32ExitCode = 0; 212 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 213 SetServiceStatus(hStatus, &ServiceStatus); 214 return; 215 216 case SERVICE_CONTROL_SHUTDOWN: 217 DPRINT("W32Time Service stopped.\n"); 218 219 ServiceStatus.dwWin32ExitCode = 0; 220 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 221 SetServiceStatus(hStatus, &ServiceStatus); 222 return; 223 224 default: 225 break; 226 } 227 228 /* Report current status */ 229 SetServiceStatus(hStatus, &ServiceStatus); 230 231 return; 232 } 233 234 235 VOID 236 WINAPI 237 ServiceMain(DWORD argc, LPWSTR *argv) 238 { 239 int result; 240 DWORD dwPollInterval; 241 242 UNREFERENCED_PARAMETER(argc); 243 UNREFERENCED_PARAMETER(argv); 244 245 ServiceStatus.dwServiceType = SERVICE_WIN32; 246 ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 247 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 248 ServiceStatus.dwWin32ExitCode = 0; 249 ServiceStatus.dwServiceSpecificExitCode = 0; 250 ServiceStatus.dwCheckPoint = 0; 251 ServiceStatus.dwWaitHint = 0; 252 253 hStatus = RegisterServiceCtrlHandlerW(ServiceName, 254 ControlHandler); 255 if (!hStatus) 256 { 257 /* Registering Control Handler failed */ 258 return; 259 } 260 261 /* We report the running status to SCM. */ 262 ServiceStatus.dwCurrentState = SERVICE_RUNNING; 263 SetServiceStatus(hStatus, &ServiceStatus); 264 265 /* The worker loop of a service */ 266 while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) 267 { 268 dwPollInterval = GetIntervalSetting(); 269 result = SetTime(); 270 271 if (result) 272 DPRINT("W32Time Service failed to set clock.\n"); 273 else 274 DPRINT("W32Time Service successfully set clock.\n"); 275 276 if (result) 277 { 278 /* In general we do not want to stop this service for a single 279 * Internet read failure but there may be other reasons for which 280 * we really might want to stop it. 281 * Therefore this code is left here to make it easy to stop this 282 * service when the correct conditions can be determined, but it 283 * is left commented out. 284 ServiceStatus.dwCurrentState = SERVICE_STOPPED; 285 ServiceStatus.dwWin32ExitCode = -1; 286 SetServiceStatus(hStatus, &ServiceStatus); 287 return; 288 */ 289 } 290 291 Sleep(dwPollInterval * 1000); 292 } 293 return; 294 } 295 296 297 298 BOOL WINAPI 299 DllMain(HINSTANCE hinstDLL, 300 DWORD fdwReason, 301 LPVOID lpvReserved) 302 { 303 switch (fdwReason) 304 { 305 case DLL_PROCESS_ATTACH: 306 DisableThreadLibraryCalls(hinstDLL); 307 break; 308 309 case DLL_PROCESS_DETACH: 310 break; 311 } 312 313 return TRUE; 314 } 315 316 317 DWORD WINAPI 318 W32TimeSyncNow(LPCWSTR cmdline, 319 UINT blocking, 320 UINT flags) 321 { 322 DWORD result; 323 result = SetTime(); 324 if (result) 325 { 326 DPRINT("W32TimeSyncNow failed and clock not set.\n"); 327 } 328 else 329 { 330 DPRINT("W32TimeSyncNow succeeded and clock set.\n"); 331 } 332 return result; 333 } 334