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
SystemSetTime(LPSYSTEMTIME lpSystemTime)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
UpdateSystemTime(ULONG ulTime)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
GetIntervalSetting(VOID)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 W32TIME_POLL_INTERVAL;
157 else
158 return dwData;
159 }
160
161
162 DWORD
SetTime(VOID)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
ControlHandler(DWORD request)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
W32TmServiceMain(DWORD argc,LPWSTR * argv)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
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)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
W32TimeSyncNow(LPCWSTR cmdline,UINT blocking,UINT flags)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