3 Copyright 2003, 2004, 2005, 2007, 2012, 2014 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 = %u",
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
);
213 log (LOG_CRIT
, "failed to create thread, error = %u", GetLastError ());
214 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR
,
215 GetLastError (), ENOMEM
);
217 ipcht_entry
= new ipc_hookthread
;
218 ipcht_entry
->thread
= thread
;
219 ipcht_entry
->winpid
= td
->ipcblk
->winpid
;
220 ipcht_entry
->vmspace
.vm_map
= NULL
;
221 ipcht_entry
->vmspace
.vm_shm
= NULL
;
222 enter_critical_section (&ipcht_cs
);
223 SLIST_INSERT_HEAD (&ipcht_list
, ipcht_entry
, sht_next
);
224 leave_critical_section (&ipcht_cs
);
229 * Need the admins group SID to compare with groups in client token.
231 PSID admininstrator_group_sid
;
234 init_admin_sid (void)
236 SID_IDENTIFIER_AUTHORITY nt_auth
= {SECURITY_NT_AUTHORITY
};
237 if (! AllocateAndInitializeSid (&nt_auth
, 2, 32, 544, 0, 0, 0, 0, 0, 0,
238 &admininstrator_group_sid
))
239 panic ("failed to create well known sids, error = %u",
243 SECURITY_DESCRIPTOR sec_all_nih_sd
;
244 SECURITY_ATTRIBUTES sec_all_nih
= { sizeof (SECURITY_ATTRIBUTES
),
251 InitializeSecurityDescriptor (&sec_all_nih_sd
, SECURITY_DESCRIPTOR_REVISION
);
252 SetSecurityDescriptorDacl (&sec_all_nih_sd
, TRUE
, 0, FALSE
);
256 /* Global vars, determining whether the IPC stuff should be started or not. */
257 tun_bool_t support_sharedmem
= TUN_UNDEF
;
258 tun_bool_t support_msgqueues
= TUN_UNDEF
;
259 tun_bool_t support_semaphores
= TUN_UNDEF
;
264 mtx_init (&Giant
, "Giant", NULL
, MTX_DEF
);
266 ipcexit_event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
268 panic ("Failed to create ipcexit event object");
269 InitializeCriticalSection (&ipcht_cs
);
270 if (support_msgqueues
== TUN_TRUE
)
272 if (support_semaphores
== TUN_TRUE
)
274 if (support_sharedmem
== TUN_TRUE
)
281 ipcexit_dispose_hookthreads ();
282 CloseHandle (ipcexit_event
);
284 if (support_semaphores
== TUN_TRUE
)
286 if (support_sharedmem
== TUN_TRUE
)
288 if (support_msgqueues
== TUN_TRUE
)
290 mtx_destroy (&Giant
);
295 * Helper function to find a gid in a list of gids.
298 is_grp_member (gid_t grp
, gid_t
*grplist
, int listsize
)
301 for (; listsize
> 0; --listsize
)
302 if (grp
== grplist
[listsize
- 1])
308 * Helper function to get a specific token information from a token.
309 * This function mallocs the necessary buffer spcae by itself. It
310 * must be free'd by the calling function.
313 get_token_info (HANDLE tok
, TOKEN_INFORMATION_CLASS tic
)
318 if (!GetTokenInformation (tok
, tic
, NULL
, 0, &size
)
319 && GetLastError () != ERROR_INSUFFICIENT_BUFFER
)
321 if (!(buf
= malloc (size
)))
323 if (!GetTokenInformation (tok
, tic
, buf
, size
, &size
))
332 * Check if client user helds "mode" permission when accessing object
333 * associated with "perm" permission record.
334 * Returns an error code.
337 ipcperm (struct thread
*td
, ipc_perm
*perm
, unsigned int mode
)
339 proc
*p
= td
->ipcblk
;
345 return (p
->uid
!= perm
->cuid
&& p
->uid
!= perm
->uid
)
348 if (p
->uid
!= perm
->cuid
&& p
->uid
!= perm
->uid
)
350 /* If the user is a member of the creator or owner group, test
351 against group bits, otherwise against other bits. */
352 mode
>>= p
->gid
!= perm
->gid
&& p
->gid
!= perm
->cgid
353 && !is_grp_member (perm
->gid
, p
->gidlist
, p
->gidcnt
)
354 && !is_grp_member (perm
->cgid
, p
->gidlist
, p
->gidcnt
)
357 return (mode
& perm
->mode
) != mode
? EACCES
: 0;
361 * Check for client user being superuser.
362 * Returns an error code.
365 suser (struct thread
*td
)
367 /* This value has been set at ImpersonateNamedPipeClient() time
368 using the token information. See adjust_identity_info() below. */
369 return td
->ipcblk
->is_admin
? 0 : EACCES
;
373 * Retrieves user and group info from impersonated token and creates the
374 * correct uid, gid, gidlist and is_admin entries in p from that.
377 adjust_identity_info (struct proc
*p
)
381 if (!OpenThreadToken (GetCurrentThread (), TOKEN_READ
, TRUE
, &tok
))
383 debug ("Failed to open worker thread access token for pid %d, winpid %d",
384 p
->cygpid
, p
->winpid
);
388 /* Get uid from user SID in token. */
390 if (!(user
= (PTOKEN_USER
)get_token_info (tok
, TokenUser
)))
392 p
->uid
= cygwin_internal (CW_GET_UID_FROM_SID
, user
->User
.Sid
);
394 if (p
->uid
== (uid_t
)-1)
395 log (LOG_WARNING
, "WARNING: User not found in /etc/passwd! Using uid -1!");
397 /* Get gid from primary group SID in token. */
398 PTOKEN_PRIMARY_GROUP pgrp
;
399 if (!(pgrp
= (PTOKEN_PRIMARY_GROUP
)get_token_info (tok
, TokenPrimaryGroup
)))
401 p
->gid
= cygwin_internal (CW_GET_GID_FROM_SID
, pgrp
->PrimaryGroup
);
403 if (p
->gid
== (gid_t
)-1)
404 log (LOG_WARNING
,"WARNING: Group not found in /etc/group! Using gid -1!");
406 /* Generate gid list from token group's SID list. Also look if the token
407 has an enabled admin group SID. That means, the process has admin
408 privileges. That knowledge is used in suser(). */
410 if (!(gsids
= (PTOKEN_GROUPS
)get_token_info (tok
, TokenGroups
)))
412 if (gsids
->GroupCount
)
414 p
->gidlist
= (gid_t
*) calloc (gsids
->GroupCount
, sizeof (gid_t
));
416 p
->gidcnt
= gsids
->GroupCount
;
418 for (DWORD i
= 0; i
< gsids
->GroupCount
; ++i
)
421 p
->gidlist
[i
] = cygwin_internal (CW_GET_GID_FROM_SID
,
422 gsids
->Groups
[i
].Sid
);
423 if (EqualSid (gsids
->Groups
[i
].Sid
, admininstrator_group_sid
)
424 && (gsids
->Groups
[i
].Attributes
& SE_GROUP_ENABLED
))
434 log (LOG_CRIT
, "Failed to get token information for pid %d, winpid %d",
435 p
->cygpid
, p
->winpid
);
440 * Windows wrapper implementation of the VM functions called by sysv_shm.cc.
444 _vm_pager_allocate (int size
, int shmflg
)
446 /* Create the file mapping object with full access for everyone. This is
447 necessary to allow later calls to shmctl(..., IPC_SET,...) to
448 change the access rights and ownership of a shared memory region.
449 The access rights are tested at the beginning of every shm... function.
450 Note that this does not influence the actual read or write access
451 defined in a call to shmat. */
452 vm_object_t object
= CreateFileMapping (INVALID_HANDLE_VALUE
, &sec_all_nih
,
453 PAGE_READWRITE
, 0, size
, NULL
);
455 panic ("CreateFileMapping in _vm_pager_allocate failed, %u", GetLastError ());
460 vm_object_duplicate (struct thread
*td
, vm_object_t object
)
462 vm_object_t dup_object
;
463 if (!DuplicateHandle (GetCurrentProcess (), object
,
464 td
->client
->handle (), &dup_object
,
465 0, TRUE
, DUPLICATE_SAME_ACCESS
))
466 panic ("!DuplicateHandle in vm_object_duplicate failed, %u", GetLastError ());
471 vm_object_deallocate (vm_object_t object
)
474 CloseHandle (object
);
478 * Tunable parameters are read from a system wide cygserver.conf file.
479 * On the first call to tunable_int_fetch, the file is read and the
480 * parameters are set accordingly. Each parameter has default, max and
484 enum tun_params_type
{
497 tun_params_type type
;
498 union tun_value value
;
501 void (*check_func
)(tun_struct
*, char *, const char *);
505 default_tun_check (tun_struct
*that
, char *value
, const char *fname
)
512 val
.ival
= strtoul (value
, &c
, 10);
513 if (!val
.ival
|| (c
&& *c
))
514 panic ("Error in config file %s: Value of parameter %s malformed",
516 if (val
.ival
< that
->min
.ival
|| val
.ival
> that
->max
.ival
)
517 panic ("Error in config file %s: Value of parameter %s must be "
518 "between %lu and %lu",
519 fname
, that
->name
, that
->min
.ival
, that
->max
.ival
);
520 if (that
->value
.ival
)
521 panic ("Error in config file %s: Parameter %s set twice.\n",
523 that
->value
.ival
= val
.ival
;
526 if (!strcasecmp (value
, "no") || !strcasecmp (value
, "n")
527 || !strcasecmp (value
, "false") || !strcasecmp (value
, "f")
528 || !strcasecmp (value
, "0"))
529 val
.bval
= TUN_FALSE
;
530 else if (!strcasecmp (value
, "yes") || !strcasecmp (value
, "y")
531 || !strcasecmp (value
, "true") || !strcasecmp (value
, "t")
532 || !strcasecmp (value
, "1"))
535 panic ("Error in config file %s: Value of parameter %s malformed\n"
536 "Allowed values: \"yes\", \"no\", \"y\", \"n\", \"true\", \"false\", \"t\", \"f\", \"1\" and \"0\"", fname
, that
->name
);
537 that
->value
.bval
= val
.bval
;
540 /* Shouldn't happen. */
541 panic ("Internal error: Wrong type of tunable parameter");
546 static tun_struct tunable_params
[] =
549 { "kern.srv.cleanup_threads", TUN_INT
, {0}, {1}, {32}, default_tun_check
},
550 { "kern.srv.request_threads", TUN_INT
, {0}, {1}, {310}, default_tun_check
},
551 { "kern.srv.process_cache_size", TUN_INT
, {0}, {1}, {310}, default_tun_check
},
552 { "kern.srv.sharedmem", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
553 { "kern.srv.msgqueues", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
554 { "kern.srv.semaphores", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
557 { "kern.log.syslog", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
558 { "kern.log.stderr", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
559 { "kern.log.debug", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
560 { "kern.log.level", TUN_INT
, {0}, {1}, {7}, default_tun_check
},
563 { "kern.ipc.msgseg", TUN_INT
, {0}, {256}, {65535}, default_tun_check
},
564 { "kern.ipc.msgssz", TUN_INT
, {0}, {8}, {1024}, default_tun_check
},
565 { "kern.ipc.msgmnb", TUN_INT
, {0}, {1}, {65535}, default_tun_check
},
566 { "kern.ipc.msgmni", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
567 { "kern.ipc.msgtql", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
570 //{ "kern.ipc.semmap", TUN_INT, {0}, {1}, {1024}, default_tun_check},
571 { "kern.ipc.semmni", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
572 { "kern.ipc.semmns", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
573 { "kern.ipc.semmnu", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
574 { "kern.ipc.semmsl", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
575 { "kern.ipc.semopm", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
576 { "kern.ipc.semume", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
577 //{ "kern.ipc.semusz", TUN_INT, {0}, {1}, {1024}, default_tun_check},
578 { "kern.ipc.semvmx", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
579 { "kern.ipc.semaem", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
582 { "kern.ipc.shmmaxpgs", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
583 //{ "kern.ipc.shmmin", TUN_INT, {0}, {1}, {32767}, default_tun_check},
584 { "kern.ipc.shmmni", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
585 { "kern.ipc.shmseg", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
586 { "kern.ipc.shm_allow_removed", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
587 //{ "kern.ipc.shm_use_phys", TUN_INT, {0}, {1}, {32767}, default_tun_check},
588 { NULL
, TUN_NULL
, {0}, {0}, {0}, NULL
}
591 #define skip_whitespace(c) while (*(c) && isspace (*(c))) ++(c)
592 #define skip_nonwhitespace(c) while (*(c) && !isspace (*(c)) && *(c) != '#') ++(c)
593 #define end_of_content(c) (!*(c) || *(c) == '#')
596 tunable_param_init (const char *config_file
, bool force
)
598 FILE *fp
= fopen (config_file
, "rt");
602 panic ("can't open config file %s\n", config_file
);
606 while (fgets (line
, 1024, fp
))
608 char *c
= strrchr (line
, '\n');
610 panic ("Line too long in confg file %s\n", config_file
);
611 /* Overwrite trailing NL. */
615 if (end_of_content (c
))
617 /* So we are on the first character of a parameter name. */
619 /* Find end of name. */
620 skip_nonwhitespace (c
);
621 if (end_of_content (c
))
624 panic ("Error in config file %s: Parameter %s has no value.\n",
627 /* Mark end of name. */
630 if (end_of_content (c
))
631 panic ("Error in config file %s: Parameter %s has no value.\n",
633 /* Now we are on the first character of a parameter's value. */
635 /* This only works for simple parameters. If complex string parameters
636 are added at one point, the scanning routine must be changed here. */
637 /* Find end of value. */
638 skip_nonwhitespace (c
);
639 /* Mark end of value. */
641 /* Now look if name is one from our list. */
643 for (s
= &tunable_params
[0]; s
->name
; ++s
)
644 if (!strcmp (name
, s
->name
))
646 /* Now read value and check for validity. check_func doesn't
648 s
->check_func (s
, value
, config_file
);
652 panic ("Error in config file %s: Unknown parameter %s.\n",
659 tunable_int_fetch (const char *name
, int32_t *tunable_target
)
662 for (s
= &tunable_params
[0]; s
->name
; ++s
)
663 if (!strcmp (name
, s
->name
))
665 if (!s
) /* Not found */
667 if (s
->type
!= TUN_INT
) /* Wrong type */
669 if (!s
->value
.ival
) /* Not set in config file */
671 *tunable_target
= s
->value
.ival
;
672 debug ("\nSet %s to %u\n", name
, *tunable_target
);
676 tunable_bool_fetch (const char *name
, tun_bool_t
*tunable_target
)
679 const char *tun_bool_val_string
[] = { "undefined", "no", "yes" };
680 for (s
= &tunable_params
[0]; s
->name
; ++s
)
681 if (!strcmp (name
, s
->name
))
683 if (!s
) /* Not found */
685 if (s
->type
!= TUN_BOOL
) /* Wrong type */
687 if (!s
->value
.ival
) /* Not set in config file */
689 *tunable_target
= s
->value
.bval
;
690 debug ("\nSet %s to %s\n", name
, tun_bool_val_string
[*tunable_target
]);
692 #endif /* __OUTSIDE_CYGWIN__ */