1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Cygwin-specific code. December 2002. Updated Jan 2015.
6 This is prefixed to the src/os.c file.
8 This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
11 /* We need a special mkdir that
12 allows names starting with // */
14 int cygwin_mkdir( const char *path, mode_t mode )
16 const char * p = path;
17 if (*p == '/') while(*(p+1) == '/') p++;
18 return mkdir(p, mode);
21 #ifndef COMPILE_UTILITY /* Utilities don't need special code */
24 #include "../pam/pam.c"
28 unsigned int cygwin_WinVersion;
30 /* Conflict between Windows definitions and others */
42 #define EqualLuid(Luid1, Luid2) \
43 ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
44 #include <sys/cygwin.h>
46 /* Special static variables */
47 static BOOL cygwin_debug = FALSE;
48 static int fakesetugid = 1; /* when not privileged, setugid = noop */
51 int cygwin_setuid(uid_t uid )
54 if (fakesetugid == 0) {
57 fprintf(stderr, "setuid %u %u %d pid: %d\n",
58 uid, getuid(),res, getpid());
64 int cygwin_setgid(gid_t gid )
67 if (fakesetugid == 0) {
70 fprintf(stderr, "setgid %u %u %d pid: %d\n",
71 gid, getgid(), res, getpid());
76 /* Background processes run at lower priority */
77 static void cygwin_setpriority()
79 if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
80 SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
86 MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
88 Next byte: minor version of OS
89 Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
90 //#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */
91 //#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */
94 Routine to find if process or thread is privileged
101 static DWORD get_privileges ()
105 HANDLE hToken = NULL;
106 PTOKEN_PRIVILEGES privs;
110 privs = (PTOKEN_PRIVILEGES) buffer;
112 if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
113 && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
114 && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
115 && (GetTokenInformation( hToken, TokenPrivileges,
116 privs, sizeof (buffer), &length)
117 || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
118 && (privs = (PTOKEN_PRIVILEGES) alloca (length))
119 && GetTokenInformation(hToken, TokenPrivileges,
120 privs, length, &length)))) {
121 for (i = 0; i < privs->PrivilegeCount; i++) {
122 if (EqualLuid(privs->Privileges[i].Luid, cluid))
124 if (ret == (CREATE_BIT))
129 fprintf(stderr, "has_create_token_privilege %u\n", GetLastError());
138 We use cygwin_premain to fake a few things
139 and to provide some debug info
141 void cygwin_premain2(int argc, char ** argv, struct per_process * ptr)
143 int i, res, is_daemon = 0, is_spoolwritable, is_privileged, is_eximuser;
144 uid_t myuid, systemuid;
145 gid_t mygid, adminsgid;
146 struct passwd * pwp = NULL;
149 SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
150 SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
155 cygwin_WinVersion = GetVersion();
156 if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
157 /* Produce some debugging on stderr,
158 cannot yet use exim's debug functions.
159 Exim does not use -c and ignores -n.
160 Set lower priority for daemons */
161 for (i = 1; i < argc; i++) {
162 if (argv[i][0] == '-') {
163 if (argv[i][1] == 'c') {
166 argv[i][1] = 'n'; /* Replace -c by -n */
168 fprintf(stderr, "CYGWIN = \"%s\".\n", cygenv);
169 if (((size = cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, 0)) > 0)
170 && ((win32_path = malloc(size)) != NULL)
171 && (cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, size) == 0)) {
172 fprintf(stderr, " Root / mapped to %ls.\n", win32_path);
176 else if (argv[i][1] == 'b' && argv[i][2] == 'd') {
178 cygwin_setpriority();
184 We initially set the exim uid & gid to those of the "exim user",
185 or to the root uid (SYSTEM) and exim gid (ADMINS),
186 If privileged, we setuid to those.
187 We always set the configure uid to the system uid.
188 We always set the root uid to the real uid
189 to allow exim imposed restrictions (bypassable by recompiling)
190 and to avoid exec that cause loss of privilege
191 If not privileged and unable to chown,
192 we set the exim uid to our uid.
193 If unprivileged and /var/spool/exim is writable and not running as listening daemon,
194 we fake all subsequent setuid. */
196 /* Get the system and admins uid from their sids */
197 if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1) {
198 fprintf(stderr, "Cannot map System sid. Aborting\n");
201 if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1) {
202 fprintf(stderr, "Cannot map Admins sid. Aborting\n");
206 priv_flags = get_privileges ();
207 is_privileged = !!(priv_flags & CREATE_BIT);
209 /* Call getpwnam for account exim after getting the local exim name */
210 char exim_username[DNLEN + UNLEN + 2];
211 if (cygwin_internal(CW_CYGNAME_FROM_WINNAME, "exim", exim_username, sizeof exim_username) != 0)
212 pwp = getpwnam (exim_username);
214 /* If cannot setuid to exim or and is not the daemon (which is assumed to be
215 able to chown or to be the exim user) set the exim ugid to our ugid to avoid
216 chown failures after creating files and to be able to setuid to exim in
217 exim.c ( "privilege not needed" ). */
218 if ((is_privileged == 0) && (!is_daemon)) {
222 else if (pwp != NULL) {
223 exim_uid = pwp->pw_uid; /* Set it according to passwd */
224 exim_gid = pwp->pw_gid;
228 exim_uid = systemuid;
229 exim_gid = adminsgid;
233 res = stat("/var/spool/exim", &buf);
234 /* Check if writable (and can be stat) */
235 is_spoolwritable = ((res == 0) && ((buf.st_mode & S_IWOTH) != 0));
237 fakesetugid = (is_privileged == 0) && (is_daemon == 0) && (is_spoolwritable == 1);
239 if (is_privileged) { /* Can setuid */
240 if (cygwin_setgid(exim_gid) /* Setuid to exim */
241 || cygwin_setuid(exim_uid)) {
242 fprintf(stderr, "Unable to setuid/gid to exim. priv_flags: %x\n", priv_flags);
243 exit(0); /* Problem... Perhaps not in 544 */
247 /* Set the configuration file uid and gid to the system uid and admins gid. */
248 config_uid = systemuid;
249 config_gid = adminsgid;
251 /* Pretend we are root to avoid useless exec
252 and avoid exim set limitations.
253 We are limited by file access rights */
254 root_uid = getuid ();
257 fprintf(stderr, "Starting uid %u, gid %u, priv_flags %x, is_privileged %d, is_daemon %d, is_spoolwritable %d.\n",
258 myuid, mygid, priv_flags, is_privileged, is_daemon, is_spoolwritable);
259 fprintf(stderr, "root_uid %u, exim_uid %u, exim_gid %u, config_uid %u, config_gid %u, is_eximuser %d.\n",
260 root_uid, exim_uid, exim_gid, config_uid, config_gid, is_eximuser);
265 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
266 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
268 /*****************************************************************
269 Functions for average load measurements
271 Uses NtQuerySystemInformation.
272 This requires definitions that are not part of
273 standard include files.
275 This is discouraged starting with WinXP.
277 *************************************************************/
278 /* Structure to compute the load average efficiently */
281 unsigned long long Time100ns; /* Last measurement time */
282 unsigned long long IdleCount; /* Latest cumulative idle time */
283 unsigned long long LastCounter; /* Last measurement counter */
284 unsigned long long PerfFreq; /* Perf counter frequency */
285 int LastLoad; /* Last reported load, or -1 */
292 } cygwin_load = {NULL, 0, NULL};
296 typedef enum _SYSTEM_INFORMATION_CLASS
298 SystemBasicInformation = 0,
299 SystemPerformanceInformation = 2,
300 SystemTimeOfDayInformation = 3,
301 SystemProcessesAndThreadsInformation = 5,
302 SystemProcessorTimes = 8,
303 SystemPagefileInformation = 18,
304 /* There are a lot more of these... */
305 } SYSTEM_INFORMATION_CLASS;
307 typedef struct _SYSTEM_BASIC_INFORMATION
310 ULONG MaximumIncrement;
311 ULONG PhysicalPageSize;
312 ULONG NumberOfPhysicalPages;
313 ULONG LowestPhysicalPage;
314 ULONG HighestPhysicalPage;
315 ULONG AllocationGranularity;
316 ULONG LowestUserAddress;
317 ULONG HighestUserAddress;
318 ULONG ActiveProcessors;
319 UCHAR NumberProcessors;
320 } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
322 typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
324 LARGE_INTEGER IdleTime;
325 LARGE_INTEGER KernelTime;
326 LARGE_INTEGER UserTime;
327 LARGE_INTEGER DpcTime;
328 LARGE_INTEGER InterruptTime;
329 ULONG InterruptCount;
330 } SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
332 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
333 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
335 static NtQuerySystemInformation_t NtQuerySystemInformation;
336 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
338 /*****************************************************************
341 Load special functions from the NTDLL
342 Return TRUE if success.
344 *****************************************************************/
346 static BOOL LoadNtdll()
350 if ((hinstLib = LoadLibrary("NTDLL.DLL"))
351 && (NtQuerySystemInformation =
352 (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
353 "NtQuerySystemInformation"))
354 && (RtlNtStatusToDosError =
355 (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
356 "RtlNtStatusToDosError")))
360 debug_printf("perf: load: %u (Windows)\n", GetLastError());
363 /*****************************************************************
366 Measures current Time100ns and IdleCount
367 Return TRUE if success.
369 *****************************************************************/
371 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
372 unsigned long long int *IdleCountPtr)
375 SYSTEM_BASIC_INFORMATION sbi;
376 PSYSTEM_PROCESSOR_TIMES spt;
378 *Time100nsPtr = *IdleCountPtr = 0;
380 if ((ret = NtQuerySystemInformation(SystemBasicInformation,
381 (PVOID) &sbi, sizeof sbi, NULL))
384 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
385 RtlNtStatusToDosError(ret));
387 else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
389 debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
391 else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
392 sizeof spt[0] * sbi.NumberProcessors, NULL))
395 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
396 RtlNtStatusToDosError(ret));
400 for (i = 0; i < sbi.NumberProcessors; i++) {
401 *Time100nsPtr += spt[i].KernelTime.QuadPart;;
402 *Time100nsPtr += spt[i].UserTime.QuadPart;
403 *IdleCountPtr += spt[i].IdleTime.QuadPart;
410 /*****************************************************************
413 Initialize the cygwin_load.perf structure.
414 and set cygwin_load.perf->Flag to TRUE if successful.
415 This is called the first time os_getloadavg is called
416 *****************************************************************/
417 static void InitLoadAvg(cygwin_perf_t *this)
421 /* Get perf frequency and counter */
422 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
423 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
425 /* Get initial values for Time100ns and IdleCount */
427 && ReadStat( & this->Time100ns,
429 /* If success, set the Load to 0, else to -1 */
430 if (success) this->LastLoad = 0;
432 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
438 /*****************************************************************
442 Return -1 if not available;
443 Return the previous value if less than AVERAGING sec old.
444 else return the processor load on a [0 - 1000] scale.
446 The first time we are called we initialize the counts
448 The initial load cannot be measured as we use the processor 100%
449 *****************************************************************/
450 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
455 unsigned long long Time100ns, IdleCount, CurrCounter;
460 Reload the dlls and the file mapping */
461 if ((newpid = getpid()) != cygwin_load.pid) {
463 cygwin_load.pid = newpid;
466 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
467 cygwin_load.perf = NULL;
471 if ((new = !cygwin_load.handle)) {
472 cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
473 0, sizeof(cygwin_perf_t), NULL);
475 debug_printf("Perf: CreateFileMapping: handle %p\n", (void *) cygwin_load.handle);
477 cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
478 FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
480 debug_printf("Perf: MapViewOfFile: addr %p\n", (void *) cygwin_load.perf);
481 if (new && cygwin_load.perf)
482 InitLoadAvg(cygwin_load.perf);
485 /* Check if initialized OK */
486 if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
489 /* If we cannot get the lock, we return 0.
490 This is to prevent any lock-up possibility.
491 Finding a lock busy is unlikely, and giving up only
492 results in an immediate delivery .*/
494 if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
496 debug_printf("Perf: Lock busy\n");
500 /* Get the current time (PerfCounter) */
501 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
502 /* Calls closer than AVERAGING sec apart use the previous value */
503 if (CurrCounter - cygwin_load.perf->LastCounter >
504 AVERAGING * cygwin_load.perf->PerfFreq) {
505 /* Get Time100ns and IdleCount */
506 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
507 /* Return processor load on 1000 scale */
508 value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
509 (Time100ns - cygwin_load.perf->Time100ns));
510 cygwin_load.perf->Time100ns = Time100ns;
511 cygwin_load.perf->IdleCount = IdleCount;
512 cygwin_load.perf->LastCounter = CurrCounter;
513 cygwin_load.perf->LastLoad = value;
515 debug_printf("Perf: New load average %d\n", value);
517 else { /* Something bad happened.
518 Refuse to measure the load anymore
519 but don't bother releasing the buffer */
520 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
521 cygwin_load.perf->LastLoad = -1;
526 debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad);
527 cygwin_load.perf->Lock = 0;
528 return cygwin_load.perf->LastLoad;
530 #endif /* OS_LOAD_AVERAGE */
531 #endif /* COMPILE_UTILITY */