1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4 /* SPDX-License-Identifier: GPL-2.0-or-later */
6 /* Cygwin-specific code. December 2002. Updated Jan 2015.
7 This is prefixed to the src/os.c file.
9 This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
12 /* We need a special mkdir that
13 allows names starting with // */
15 int cygwin_mkdir( const char *path, mode_t mode )
17 const char * p = path;
18 if (*p == '/') while(*(p+1) == '/') p++;
19 return mkdir(p, mode);
22 #ifndef COMPILE_UTILITY /* Utilities don't need special code */
25 #include "../pam/pam.c"
29 unsigned int cygwin_WinVersion;
31 /* Conflict between Windows definitions and others */
43 #define EqualLuid(Luid1, Luid2) \
44 ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
45 #include <sys/cygwin.h>
47 /* Special static variables */
48 static BOOL cygwin_debug = FALSE;
49 static int fakesetugid = 1; /* when not privileged, setugid = noop */
52 int cygwin_setuid(uid_t uid )
55 if (fakesetugid == 0) {
58 fprintf(stderr, "setuid %u %u %d pid: %d\n",
59 uid, getuid(),res, getpid());
65 int cygwin_setgid(gid_t gid )
68 if (fakesetugid == 0) {
71 fprintf(stderr, "setgid %u %u %d pid: %d\n",
72 gid, getgid(), res, getpid());
77 /* Background processes run at lower priority */
78 static void cygwin_setpriority()
80 if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
81 SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
87 MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
89 Next byte: minor version of OS
90 Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
91 //#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */
92 //#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */
95 Routine to find if process or thread is privileged
102 static DWORD get_privileges ()
106 HANDLE hToken = NULL;
107 PTOKEN_PRIVILEGES privs;
111 privs = (PTOKEN_PRIVILEGES) buffer;
113 if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
114 && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
115 && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
116 && (GetTokenInformation( hToken, TokenPrivileges,
117 privs, sizeof (buffer), &length)
118 || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
119 && (privs = (PTOKEN_PRIVILEGES) alloca (length))
120 && GetTokenInformation(hToken, TokenPrivileges,
121 privs, length, &length)))) {
122 for (i = 0; i < privs->PrivilegeCount; i++) {
123 if (EqualLuid(privs->Privileges[i].Luid, cluid))
125 if (ret == (CREATE_BIT))
130 fprintf(stderr, "has_create_token_privilege %u\n", GetLastError());
139 We use cygwin_premain to fake a few things
140 and to provide some debug info
142 void cygwin_premain2(int argc, char ** argv, struct per_process * ptr)
144 int i, res, is_daemon = 0, is_spoolwritable, is_privileged, is_eximuser;
145 uid_t myuid, systemuid;
146 gid_t mygid, adminsgid;
147 struct passwd * pwp = NULL;
150 SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
151 SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
156 cygwin_WinVersion = GetVersion();
157 if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
158 /* Produce some debugging on stderr,
159 cannot yet use exim's debug functions.
160 Exim does not use -c and ignores -n.
161 Set lower priority for daemons */
162 for (i = 1; i < argc; i++) {
163 if (argv[i][0] == '-') {
164 if (argv[i][1] == 'c') {
167 argv[i][1] = 'n'; /* Replace -c by -n */
169 fprintf(stderr, "CYGWIN = \"%s\".\n", cygenv);
170 if (((size = cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, 0)) > 0)
171 && ((win32_path = store_malloc(size)) != NULL)
172 && (cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, size) == 0)) {
173 fprintf(stderr, " Root / mapped to %ls.\n", win32_path);
174 store_free(win32_path);
177 else if (argv[i][1] == 'b' && argv[i][2] == 'd') {
179 cygwin_setpriority();
185 We initially set the exim uid & gid to those of the "exim user",
186 or to the root uid (SYSTEM) and exim gid (ADMINS),
187 If privileged, we setuid to those.
188 We always set the configure uid to the system uid.
189 We always set the root uid to the real uid
190 to allow exim imposed restrictions (bypassable by recompiling)
191 and to avoid exec that cause loss of privilege
192 If not privileged and unable to chown,
193 we set the exim uid to our uid.
194 If unprivileged and /var/spool/exim is writable and not running as listening daemon,
195 we fake all subsequent setuid. */
197 /* Get the system and admins uid from their sids */
198 if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1) {
199 fprintf(stderr, "Cannot map System sid. Aborting\n");
202 if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1) {
203 fprintf(stderr, "Cannot map Admins sid. Aborting\n");
207 priv_flags = get_privileges ();
208 is_privileged = !!(priv_flags & CREATE_BIT);
210 /* Call getpwnam for account exim after getting the local exim name */
211 char exim_username[DNLEN + UNLEN + 2];
212 if (cygwin_internal(CW_CYGNAME_FROM_WINNAME, "exim", exim_username, sizeof exim_username) != 0)
213 pwp = getpwnam (exim_username);
215 /* If cannot setuid to exim or and is not the daemon (which is assumed to be
216 able to chown or to be the exim user) set the exim ugid to our ugid to avoid
217 chown failures after creating files and to be able to setuid to exim in
218 exim.c ( "privilege not needed" ). */
219 if ((is_privileged == 0) && (!is_daemon)) {
223 else if (pwp != NULL) {
224 exim_uid = pwp->pw_uid; /* Set it according to passwd */
225 exim_gid = pwp->pw_gid;
229 exim_uid = systemuid;
230 exim_gid = adminsgid;
234 res = stat("/var/spool/exim", &buf);
235 /* Check if writable (and can be stat) */
236 is_spoolwritable = ((res == 0) && ((buf.st_mode & S_IWOTH) != 0));
238 fakesetugid = (is_privileged == 0) && (is_daemon == 0) && (is_spoolwritable == 1);
240 if (is_privileged) { /* Can setuid */
241 if (cygwin_setgid(exim_gid) /* Setuid to exim */
242 || cygwin_setuid(exim_uid)) {
243 fprintf(stderr, "Unable to setuid/gid to exim. priv_flags: %x\n", priv_flags);
244 exit(0); /* Problem... Perhaps not in 544 */
248 /* Set the configuration file uid and gid to the system uid and admins gid. */
249 config_uid = systemuid;
250 config_gid = adminsgid;
252 /* Pretend we are root to avoid useless exec
253 and avoid exim set limitations.
254 We are limited by file access rights */
255 root_uid = getuid ();
258 fprintf(stderr, "Starting uid %u, gid %u, priv_flags %x, is_privileged %d, is_daemon %d, is_spoolwritable %d.\n",
259 myuid, mygid, priv_flags, is_privileged, is_daemon, is_spoolwritable);
260 fprintf(stderr, "root_uid %u, exim_uid %u, exim_gid %u, config_uid %u, config_gid %u, is_eximuser %d.\n",
261 root_uid, exim_uid, exim_gid, config_uid, config_gid, is_eximuser);
266 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
267 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
269 /*****************************************************************
270 Functions for average load measurements
272 Uses NtQuerySystemInformation.
273 This requires definitions that are not part of
274 standard include files.
276 This is discouraged starting with WinXP.
278 *************************************************************/
279 /* Structure to compute the load average efficiently */
282 unsigned long long Time100ns; /* Last measurement time */
283 unsigned long long IdleCount; /* Latest cumulative idle time */
284 unsigned long long LastCounter; /* Last measurement counter */
285 unsigned long long PerfFreq; /* Perf counter frequency */
286 int LastLoad; /* Last reported load, or -1 */
293 } cygwin_load = {NULL, 0, NULL};
297 typedef enum _SYSTEM_INFORMATION_CLASS
299 SystemBasicInformation = 0,
300 SystemPerformanceInformation = 2,
301 SystemTimeOfDayInformation = 3,
302 SystemProcessesAndThreadsInformation = 5,
303 SystemProcessorTimes = 8,
304 SystemPagefileInformation = 18,
305 /* There are a lot more of these... */
306 } SYSTEM_INFORMATION_CLASS;
308 typedef struct _SYSTEM_BASIC_INFORMATION
311 ULONG MaximumIncrement;
312 ULONG PhysicalPageSize;
313 ULONG NumberOfPhysicalPages;
314 ULONG LowestPhysicalPage;
315 ULONG HighestPhysicalPage;
316 ULONG AllocationGranularity;
317 ULONG LowestUserAddress;
318 ULONG HighestUserAddress;
319 ULONG ActiveProcessors;
320 UCHAR NumberProcessors;
321 } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
323 typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
325 LARGE_INTEGER IdleTime;
326 LARGE_INTEGER KernelTime;
327 LARGE_INTEGER UserTime;
328 LARGE_INTEGER DpcTime;
329 LARGE_INTEGER InterruptTime;
330 ULONG InterruptCount;
331 } SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
333 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
334 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
336 static NtQuerySystemInformation_t NtQuerySystemInformation;
337 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
339 /*****************************************************************
342 Load special functions from the NTDLL
343 Return TRUE if success.
345 *****************************************************************/
347 static BOOL LoadNtdll()
351 if ((hinstLib = LoadLibrary("NTDLL.DLL"))
352 && (NtQuerySystemInformation =
353 (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
354 "NtQuerySystemInformation"))
355 && (RtlNtStatusToDosError =
356 (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
357 "RtlNtStatusToDosError")))
361 debug_printf("perf: load: %u (Windows)\n", GetLastError());
364 /*****************************************************************
367 Measures current Time100ns and IdleCount
368 Return TRUE if success.
370 *****************************************************************/
372 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
373 unsigned long long int *IdleCountPtr)
376 SYSTEM_BASIC_INFORMATION sbi;
377 PSYSTEM_PROCESSOR_TIMES spt;
379 *Time100nsPtr = *IdleCountPtr = 0;
381 if ((ret = NtQuerySystemInformation(SystemBasicInformation,
382 (PVOID) &sbi, sizeof sbi, NULL))
385 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
386 RtlNtStatusToDosError(ret));
388 else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
390 debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
392 else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
393 sizeof spt[0] * sbi.NumberProcessors, NULL))
396 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
397 RtlNtStatusToDosError(ret));
401 for (i = 0; i < sbi.NumberProcessors; i++) {
402 *Time100nsPtr += spt[i].KernelTime.QuadPart;;
403 *Time100nsPtr += spt[i].UserTime.QuadPart;
404 *IdleCountPtr += spt[i].IdleTime.QuadPart;
411 /*****************************************************************
414 Initialize the cygwin_load.perf structure.
415 and set cygwin_load.perf->Flag to TRUE if successful.
416 This is called the first time os_getloadavg is called
417 *****************************************************************/
418 static void InitLoadAvg(cygwin_perf_t *this)
422 /* Get perf frequency and counter */
423 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
424 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
426 /* Get initial values for Time100ns and IdleCount */
428 && ReadStat( & this->Time100ns,
430 /* If success, set the Load to 0, else to -1 */
431 if (success) this->LastLoad = 0;
433 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
439 /*****************************************************************
443 Return -1 if not available;
444 Return the previous value if less than AVERAGING sec old.
445 else return the processor load on a [0 - 1000] scale.
447 The first time we are called we initialize the counts
449 The initial load cannot be measured as we use the processor 100%
450 *****************************************************************/
451 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
456 unsigned long long Time100ns, IdleCount, CurrCounter;
461 Reload the dlls and the file mapping */
462 if ((newpid = getpid()) != cygwin_load.pid) {
464 cygwin_load.pid = newpid;
467 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
468 cygwin_load.perf = NULL;
472 if ((new = !cygwin_load.handle)) {
473 cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
474 0, sizeof(cygwin_perf_t), NULL);
476 debug_printf("Perf: CreateFileMapping: handle %p\n", (void *) cygwin_load.handle);
478 cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
479 FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
481 debug_printf("Perf: MapViewOfFile: addr %p\n", (void *) cygwin_load.perf);
482 if (new && cygwin_load.perf)
483 InitLoadAvg(cygwin_load.perf);
486 /* Check if initialized OK */
487 if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
490 /* If we cannot get the lock, we return 0.
491 This is to prevent any lock-up possibility.
492 Finding a lock busy is unlikely, and giving up only
493 results in an immediate delivery .*/
495 if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
497 debug_printf("Perf: Lock busy\n");
501 /* Get the current time (PerfCounter) */
502 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
503 /* Calls closer than AVERAGING sec apart use the previous value */
504 if (CurrCounter - cygwin_load.perf->LastCounter >
505 AVERAGING * cygwin_load.perf->PerfFreq) {
506 /* Get Time100ns and IdleCount */
507 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
508 /* Return processor load on 1000 scale */
509 value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
510 (Time100ns - cygwin_load.perf->Time100ns));
511 cygwin_load.perf->Time100ns = Time100ns;
512 cygwin_load.perf->IdleCount = IdleCount;
513 cygwin_load.perf->LastCounter = CurrCounter;
514 cygwin_load.perf->LastLoad = value;
516 debug_printf("Perf: New load average %d\n", value);
518 else { /* Something bad happened.
519 Refuse to measure the load anymore
520 but don't bother releasing the buffer */
521 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
522 cygwin_load.perf->LastLoad = -1;
527 debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad);
528 cygwin_load.perf->Lock = 0;
529 return cygwin_load.perf->LastLoad;
531 #endif /* OS_LOAD_AVERAGE */
532 #endif /* COMPILE_UTILITY */