3 Copyright 2003, 2004, 2005, 2007 Red Hat Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10 #ifdef __OUTSIDE_CYGWIN__
14 #define __BSD_VISIBLE 1
15 #include <sys/smallprint.h>
16 #include <sys/cygwin.h>
18 #include <sys/param.h>
20 #include <sys/queue.h>
27 #include "cygserver.h"
29 #include "cygserver_ipc.h"
30 #include "cygserver_msg.h"
31 #include "cygserver_sem.h"
32 #include "cygserver_shm.h"
35 * Copy a piece of memory from the client process into the server process.
36 * Returns an error code.
39 win_copyin (struct thread *td, const void *client_src,
40 void *server_tgt, size_t len)
42 if (!ReadProcessMemory (td->client->handle (), client_src, server_tgt,
44 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR,
45 GetLastError (), EINVAL);
50 * Copy a piece of memory from the server process into the client process.
51 * Returns an error code.
54 win_copyout (struct thread *td, const void *server_src,
55 void *client_tgt, size_t len)
57 if (!WriteProcessMemory (td->client->handle (), client_tgt, server_src,
59 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR,
60 GetLastError (), EINVAL);
64 #define enter_critical_section(c) _enter_critical_section((c),__FILE__,__LINE__)
66 _enter_critical_section (LPCRITICAL_SECTION pcs, const char *file, int line)
68 _log (file, line, LOG_DEBUG, "Try enter critical section(%p)", pcs);
69 EnterCriticalSection (pcs);
70 _log (file, line, LOG_DEBUG, "Entered critical section(%p)", pcs);
73 #define leave_critical_section(c) _leave_critical_section((c),__FILE__,__LINE__)
75 _leave_critical_section (LPCRITICAL_SECTION pcs, const char *file, int line)
77 LeaveCriticalSection (pcs);
78 _log (file, line, LOG_DEBUG, "Left critical section(%p)", pcs);
81 CRITICAL_SECTION ipcht_cs;
83 struct ipc_hookthread_storage {
88 struct ipc_hookthread {
89 SLIST_ENTRY (ipc_hookthread) sht_next;
92 struct vmspace vmspace;
94 static SLIST_HEAD (, ipc_hookthread) ipcht_list; /* list of hook threads */
96 static HANDLE ipcexit_event;
99 ipc_p_vmspace (struct proc *proc)
101 struct vmspace *ret = NULL;
102 ipc_hookthread *ipcht_entry;
103 enter_critical_section (&ipcht_cs);
104 SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next)
106 if (ipcht_entry->winpid == proc->winpid)
108 ret = proc->p_vmspace = &ipcht_entry->vmspace;
112 leave_critical_section (&ipcht_cs);
117 ipcexit_hookthread (const LPVOID param)
119 ipc_hookthread_storage *shs = (ipc_hookthread_storage *) param;
120 HANDLE obj[2] = { ipcexit_event, shs->process_hdl };
121 switch (WaitForMultipleObjects (2, obj, FALSE, INFINITE))
124 /* Cygserver shutdown. */
126 case WAIT_OBJECT_0 + 1:
127 /* Process exited. Call semexit_myhook to handle SEM_UNDOs for the
128 exiting process and shmexit_myhook to keep track of shared
130 if (Giant.owner == shs->ipcblk.winpid)
132 if (support_semaphores == TUN_TRUE)
133 semexit_myhook (NULL, &shs->ipcblk);
134 if (support_sharedmem == TUN_TRUE)
136 _mtx_lock (&Giant, shs->ipcblk.winpid, __FILE__, __LINE__);
137 ipc_p_vmspace (&shs->ipcblk);
138 shmexit_myhook (shs->ipcblk.p_vmspace);
146 CloseHandle (shs->process_hdl);
147 ipc_hookthread *ipcht_entry, *sav_entry;
148 enter_critical_section (&ipcht_cs);
149 SLIST_FOREACH_SAFE (ipcht_entry, &ipcht_list, sht_next, sav_entry)
151 if (ipcht_entry->winpid == shs->ipcblk.winpid)
153 SLIST_REMOVE (&ipcht_list, ipcht_entry, ipc_hookthread, sht_next);
154 CloseHandle (ipcht_entry->thread);
158 leave_critical_section (&ipcht_cs);
163 /* Deletes all pending hook threads. Called by ipcunload() which in turn
164 is called by the cygserver main routine. */
166 ipcexit_dispose_hookthreads (void)
168 SetEvent (ipcexit_event);
169 ipc_hookthread *ipcht_entry;
170 enter_critical_section (&ipcht_cs);
171 SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next)
173 WaitForSingleObject (ipcht_entry->thread, 1000);
174 /* Don't bother removing the linked list on cygserver shutdown. */
175 /* FIXME: Error handling? */
177 leave_critical_section (&ipcht_cs);
180 /* Creates the per process wait thread. Called by semget() under locked
181 Giant mutex condition. */
183 ipcexit_creat_hookthread (struct thread *td)
185 ipc_hookthread *ipcht_entry;
187 enter_critical_section (&ipcht_cs);
188 SLIST_FOREACH (ipcht_entry, &ipcht_list, sht_next)
190 if (ipcht_entry->winpid == td->ipcblk->winpid)
193 leave_critical_section (&ipcht_cs);
198 ipc_hookthread_storage *shs = new ipc_hookthread_storage;
199 if (!DuplicateHandle (GetCurrentProcess (), td->client->handle (),
200 GetCurrentProcess (), &shs->process_hdl,
201 0, FALSE, DUPLICATE_SAME_ACCESS))
203 log (LOG_CRIT, "failed to duplicate process handle, error = %lu",
205 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR,
206 GetLastError (), ENOMEM);
208 shs->ipcblk = *td->ipcblk;
209 HANDLE thread = CreateThread (NULL, 0, ipcexit_hookthread, shs, 0, &tid);
212 log (LOG_CRIT, "failed to create thread, error = %lu", GetLastError ());
213 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR,
214 GetLastError (), ENOMEM);
216 ipcht_entry = new ipc_hookthread;
217 ipcht_entry->thread = thread;
218 ipcht_entry->winpid = td->ipcblk->winpid;
219 ipcht_entry->vmspace.vm_map = NULL;
220 ipcht_entry->vmspace.vm_shm = NULL;
221 enter_critical_section (&ipcht_cs);
222 SLIST_INSERT_HEAD (&ipcht_list, ipcht_entry, sht_next);
223 leave_critical_section (&ipcht_cs);
228 * Need the admins group SID to compare with groups in client token.
230 PSID admininstrator_group_sid;
233 init_admin_sid (void)
235 SID_IDENTIFIER_AUTHORITY nt_auth = {SECURITY_NT_AUTHORITY};
236 if (! AllocateAndInitializeSid (&nt_auth, 2, 32, 544, 0, 0, 0, 0, 0, 0,
237 &admininstrator_group_sid))
238 panic ("failed to create well known sids, error = %lu",
242 SECURITY_DESCRIPTOR sec_all_nih_sd;
243 SECURITY_ATTRIBUTES sec_all_nih = { sizeof (SECURITY_ATTRIBUTES),
250 InitializeSecurityDescriptor (&sec_all_nih_sd, SECURITY_DESCRIPTOR_REVISION);
251 SetSecurityDescriptorDacl (&sec_all_nih_sd, TRUE, 0, FALSE);
255 /* Global vars, determining whether the IPC stuff should be started or not. */
256 tun_bool_t support_sharedmem = TUN_UNDEF;
257 tun_bool_t support_msgqueues = TUN_UNDEF;
258 tun_bool_t support_semaphores = TUN_UNDEF;
263 mtx_init (&Giant, "Giant", NULL, MTX_DEF);
265 ipcexit_event = CreateEvent (NULL, TRUE, FALSE, NULL);
267 panic ("Failed to create ipcexit event object");
268 InitializeCriticalSection (&ipcht_cs);
269 if (support_msgqueues == TUN_TRUE)
271 if (support_semaphores == TUN_TRUE)
273 if (support_sharedmem == TUN_TRUE)
280 ipcexit_dispose_hookthreads ();
281 CloseHandle (ipcexit_event);
283 if (support_semaphores == TUN_TRUE)
285 if (support_sharedmem == TUN_TRUE)
287 if (support_msgqueues == TUN_TRUE)
289 mtx_destroy (&Giant);
294 * Helper function to find a gid in a list of gids.
297 is_grp_member (gid_t grp, gid_t *grplist, int listsize)
300 for (; listsize > 0; --listsize)
301 if (grp == grplist[listsize - 1])
307 * Helper function to get a specific token information from a token.
308 * This function mallocs the necessary buffer spcae by itself. It
309 * must be free'd by the calling function.
312 get_token_info (HANDLE tok, TOKEN_INFORMATION_CLASS tic)
317 if (!GetTokenInformation (tok, tic, NULL, 0, &size)
318 && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
320 if (!(buf = malloc (size)))
322 if (!GetTokenInformation (tok, tic, buf, size, &size))
331 * Check if client user helds "mode" permission when accessing object
332 * associated with "perm" permission record.
333 * Returns an error code.
336 ipcperm (struct thread *td, ipc_perm *perm, unsigned int mode)
338 proc *p = td->ipcblk;
344 return (p->uid != perm->cuid && p->uid != perm->uid)
347 if (p->uid != perm->cuid && p->uid != perm->uid)
349 /* If the user is a member of the creator or owner group, test
350 against group bits, otherwise against other bits. */
351 mode >>= p->gid != perm->gid && p->gid != perm->cgid
352 && !is_grp_member (perm->gid, p->gidlist, p->gidcnt)
353 && !is_grp_member (perm->cgid, p->gidlist, p->gidcnt)
356 return (mode & perm->mode) != mode ? EACCES : 0;
360 * Check for client user being superuser.
361 * Returns an error code.
364 suser (struct thread *td)
366 /* This value has been set at ImpersonateNamedPipeClient() time
367 using the token information. See adjust_identity_info() below. */
368 return td->ipcblk->is_admin ? 0 : EACCES;
372 * Retrieves user and group info from impersonated token and creates the
373 * correct uid, gid, gidlist and is_admin entries in p from that.
376 adjust_identity_info (struct proc *p)
380 if (!OpenThreadToken (GetCurrentThread (), TOKEN_READ, TRUE, &tok))
382 debug ("Failed to open worker thread access token for pid %d, winpid %d",
383 p->cygpid, p->winpid);
387 /* Get uid from user SID in token. */
389 if (!(user = (PTOKEN_USER)get_token_info (tok, TokenUser)))
391 p->uid = cygwin_internal (CW_GET_UID_FROM_SID, user->User.Sid);
393 if (p->uid == (uid_t)-1)
394 log (LOG_WARNING, "WARNING: User not found in /etc/passwd! Using uid -1!");
396 /* Get gid from primary group SID in token. */
397 PTOKEN_PRIMARY_GROUP pgrp;
398 if (!(pgrp = (PTOKEN_PRIMARY_GROUP)get_token_info (tok, TokenPrimaryGroup)))
400 p->gid = cygwin_internal (CW_GET_GID_FROM_SID, pgrp->PrimaryGroup);
402 if (p->gid == (gid_t)-1)
403 log (LOG_WARNING,"WARNING: Group not found in /etc/passwd! Using gid -1!");
405 /* Generate gid list from token group's SID list. Also look if the token
406 has an enabled admin group SID. That means, the process has admin
407 privileges. That knowledge is used in suser(). */
409 if (!(gsids = (PTOKEN_GROUPS)get_token_info (tok, TokenGroups)))
411 if (gsids->GroupCount)
413 p->gidlist = (gid_t *) calloc (gsids->GroupCount, sizeof (gid_t));
415 p->gidcnt = gsids->GroupCount;
417 for (DWORD i = 0; i < gsids->GroupCount; ++i)
420 p->gidlist[i] = cygwin_internal (CW_GET_GID_FROM_SID,
421 gsids->Groups[i].Sid);
422 if (EqualSid (gsids->Groups[i].Sid, admininstrator_group_sid)
423 && (gsids->Groups[i].Attributes & SE_GROUP_ENABLED))
433 log (LOG_CRIT, "Failed to get token information for pid %d, winpid %d",
434 p->cygpid, p->winpid);
439 * Windows wrapper implementation of the VM functions called by sysv_shm.cc.
443 _vm_pager_allocate (int size, int shmflg)
445 /* Create the file mapping object with full access for everyone. This is
446 necessary to allow later calls to shmctl(..., IPC_SET,...) to
447 change the access rights and ownership of a shared memory region.
448 The access rights are tested at the beginning of every shm... function.
449 Note that this does not influence the actual read or write access
450 defined in a call to shmat. */
451 vm_object_t object = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_all_nih,
452 PAGE_READWRITE, 0, size, NULL);
454 panic ("CreateFileMapping in _vm_pager_allocate failed, %lu", GetLastError ());
459 vm_object_duplicate (struct thread *td, vm_object_t object)
461 vm_object_t dup_object;
462 if (!DuplicateHandle (GetCurrentProcess (), object,
463 td->client->handle (), &dup_object,
464 0, TRUE, DUPLICATE_SAME_ACCESS))
465 panic ("!DuplicateHandle in vm_object_duplicate failed, %lu", GetLastError ());
470 vm_object_deallocate (vm_object_t object)
473 CloseHandle (object);
477 * Tunable parameters are read from a system wide cygserver.conf file.
478 * On the first call to tunable_int_fetch, the file is read and the
479 * parameters are set accordingly. Each parameter has default, max and
483 enum tun_params_type {
496 tun_params_type type;
497 union tun_value value;
500 void (*check_func)(tun_struct *, char *, const char *);
504 default_tun_check (tun_struct *that, char *value, const char *fname)
511 val.ival = strtoul (value, &c, 10);
512 if (!val.ival || (c && *c))
513 panic ("Error in config file %s: Value of parameter %s malformed",
515 if (val.ival < that->min.ival || val.ival > that->max.ival)
516 panic ("Error in config file %s: Value of parameter %s must be "
517 "between %lu and %lu",
518 fname, that->name, that->min.ival, that->max.ival);
519 if (that->value.ival)
520 panic ("Error in config file %s: Parameter %s set twice.\n",
522 that->value.ival = val.ival;
525 if (!strcasecmp (value, "no") || !strcasecmp (value, "n")
526 || !strcasecmp (value, "false") || !strcasecmp (value, "f")
527 || !strcasecmp (value, "0"))
528 val.bval = TUN_FALSE;
529 else if (!strcasecmp (value, "yes") || !strcasecmp (value, "y")
530 || !strcasecmp (value, "true") || !strcasecmp (value, "t")
531 || !strcasecmp (value, "1"))
534 panic ("Error in config file %s: Value of parameter %s malformed\n"
535 "Allowed values: \"yes\", \"no\", \"y\", \"n\", \"true\", \"false\", \"t\", \"f\", \"1\" and \"0\"", fname, that->name);
536 that->value.bval = val.bval;
539 /* Shouldn't happen. */
540 panic ("Internal error: Wrong type of tunable parameter");
545 static tun_struct tunable_params[] =
548 { "kern.srv.cleanup_threads", TUN_INT, {0}, {1}, {32}, default_tun_check},
549 { "kern.srv.request_threads", TUN_INT, {0}, {1}, {310}, default_tun_check},
550 { "kern.srv.process_cache_size", TUN_INT, {0}, {1}, {310}, default_tun_check},
551 { "kern.srv.sharedmem", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
552 { "kern.srv.msgqueues", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
553 { "kern.srv.semaphores", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
556 { "kern.log.syslog", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
557 { "kern.log.stderr", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
558 { "kern.log.debug", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
559 { "kern.log.level", TUN_INT, {0}, {1}, {7}, default_tun_check},
562 { "kern.ipc.msgseg", TUN_INT, {0}, {256}, {65535}, default_tun_check},
563 { "kern.ipc.msgssz", TUN_INT, {0}, {8}, {1024}, default_tun_check},
564 { "kern.ipc.msgmnb", TUN_INT, {0}, {1}, {65535}, default_tun_check},
565 { "kern.ipc.msgmni", TUN_INT, {0}, {1}, {1024}, default_tun_check},
566 { "kern.ipc.msgtql", TUN_INT, {0}, {1}, {1024}, default_tun_check},
569 //{ "kern.ipc.semmap", TUN_INT, {0}, {1}, {1024}, default_tun_check},
570 { "kern.ipc.semmni", TUN_INT, {0}, {1}, {1024}, default_tun_check},
571 { "kern.ipc.semmns", TUN_INT, {0}, {1}, {1024}, default_tun_check},
572 { "kern.ipc.semmnu", TUN_INT, {0}, {1}, {1024}, default_tun_check},
573 { "kern.ipc.semmsl", TUN_INT, {0}, {1}, {1024}, default_tun_check},
574 { "kern.ipc.semopm", TUN_INT, {0}, {1}, {1024}, default_tun_check},
575 { "kern.ipc.semume", TUN_INT, {0}, {1}, {1024}, default_tun_check},
576 //{ "kern.ipc.semusz", TUN_INT, {0}, {1}, {1024}, default_tun_check},
577 { "kern.ipc.semvmx", TUN_INT, {0}, {1}, {32767}, default_tun_check},
578 { "kern.ipc.semaem", TUN_INT, {0}, {1}, {32767}, default_tun_check},
581 { "kern.ipc.shmmaxpgs", TUN_INT, {0}, {1}, {32767}, default_tun_check},
582 //{ "kern.ipc.shmmin", TUN_INT, {0}, {1}, {32767}, default_tun_check},
583 { "kern.ipc.shmmni", TUN_INT, {0}, {1}, {32767}, default_tun_check},
584 { "kern.ipc.shmseg", TUN_INT, {0}, {1}, {32767}, default_tun_check},
585 { "kern.ipc.shm_allow_removed", TUN_BOOL, {TUN_UNDEF}, {TUN_FALSE}, {TUN_TRUE}, default_tun_check},
586 //{ "kern.ipc.shm_use_phys", TUN_INT, {0}, {1}, {32767}, default_tun_check},
587 { NULL, TUN_NULL, {0}, {0}, {0}, NULL}
590 #define skip_whitespace(c) while (*(c) && isspace (*(c))) ++(c)
591 #define skip_nonwhitespace(c) while (*(c) && !isspace (*(c)) && *(c) != '#') ++(c)
592 #define end_of_content(c) (!*(c) || *(c) == '#')
595 tunable_param_init (const char *config_file, bool force)
597 FILE *fp = fopen (config_file, "rt");
601 panic ("can't open config file %s\n", config_file);
605 while (fgets (line, 1024, fp))
607 char *c = strrchr (line, '\n');
609 panic ("Line too long in confg file %s\n", config_file);
610 /* Overwrite trailing NL. */
614 if (end_of_content (c))
616 /* So we are on the first character of a parameter name. */
618 /* Find end of name. */
619 skip_nonwhitespace (c);
620 if (end_of_content (c))
623 panic ("Error in config file %s: Parameter %s has no value.\n",
626 /* Mark end of name. */
629 if (end_of_content (c))
630 panic ("Error in config file %s: Parameter %s has no value.\n",
632 /* Now we are on the first character of a parameter's value. */
634 /* This only works for simple parameters. If complex string parameters
635 are added at one point, the scanning routine must be changed here. */
636 /* Find end of value. */
637 skip_nonwhitespace (c);
638 /* Mark end of value. */
640 /* Now look if name is one from our list. */
642 for (s = &tunable_params[0]; s->name; ++s)
643 if (!strcmp (name, s->name))
645 /* Now read value and check for validity. check_func doesn't
647 s->check_func (s, value, config_file);
651 panic ("Error in config file %s: Unknown parameter %s.\n",
658 tunable_int_fetch (const char *name, long *tunable_target)
661 for (s = &tunable_params[0]; s->name; ++s)
662 if (!strcmp (name, s->name))
664 if (!s) /* Not found */
666 if (s->type != TUN_INT) /* Wrong type */
668 if (!s->value.ival) /* Not set in config file */
670 *tunable_target = s->value.ival;
671 debug ("\nSet %s to %lu\n", name, *tunable_target);
675 tunable_bool_fetch (const char *name, tun_bool_t *tunable_target)
678 const char *tun_bool_val_string[] = { "undefined", "no", "yes" };
679 for (s = &tunable_params[0]; s->name; ++s)
680 if (!strcmp (name, s->name))
682 if (!s) /* Not found */
684 if (s->type != TUN_BOOL) /* Wrong type */
686 if (!s->value.ival) /* Not set in config file */
688 *tunable_target = s->value.bval;
689 debug ("\nSet %s to %s\n", name, tun_bool_val_string[*tunable_target]);
691 #endif /* __OUTSIDE_CYGWIN__ */