3 Copyright 2003, 2004 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>
28 #include "cygserver.h"
30 #include "cygserver_ipc.h"
31 #include "cygserver_msg.h"
32 #include "cygserver_sem.h"
33 #include "cygserver_shm.h"
36 * Copy a piece of memory from the client process into the server process.
37 * Returns an error code.
40 win_copyin (struct thread
*td
, const void *client_src
,
41 void *server_tgt
, size_t len
)
43 if (!ReadProcessMemory (td
->client
->handle (), client_src
, server_tgt
,
45 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR
,
46 GetLastError (), EINVAL
);
51 * Copy a piece of memory from the server process into the client process.
52 * Returns an error code.
55 win_copyout (struct thread
*td
, const void *server_src
,
56 void *client_tgt
, size_t len
)
58 if (!WriteProcessMemory (td
->client
->handle (), client_tgt
, server_src
,
60 return cygwin_internal (CW_GET_ERRNO_FROM_WINERROR
,
61 GetLastError (), EINVAL
);
65 #define enter_critical_section(c) _enter_critical_section((c),__FILE__,__LINE__)
67 _enter_critical_section (LPCRITICAL_SECTION pcs
, const char *file
, int line
)
69 _log (file
, line
, LOG_DEBUG
, "Try enter critical section(%p)", pcs
);
70 EnterCriticalSection (pcs
);
71 _log (file
, line
, LOG_DEBUG
, "Entered critical section(%p)", pcs
);
74 #define leave_critical_section(c) _leave_critical_section((c),__FILE__,__LINE__)
76 _leave_critical_section (LPCRITICAL_SECTION pcs
, const char *file
, int line
)
78 LeaveCriticalSection (pcs
);
79 _log (file
, line
, LOG_DEBUG
, "Left critical section(%p)", pcs
);
82 CRITICAL_SECTION ipcht_cs
;
84 struct ipc_hookthread_storage
{
89 struct ipc_hookthread
{
90 SLIST_ENTRY (ipc_hookthread
) sht_next
;
93 struct vmspace vmspace
;
95 static SLIST_HEAD (, ipc_hookthread
) ipcht_list
; /* list of hook threads */
97 static HANDLE ipcexit_event
;
100 ipc_p_vmspace (struct proc
*proc
)
102 struct vmspace
*ret
= NULL
;
103 ipc_hookthread
*ipcht_entry
;
104 enter_critical_section (&ipcht_cs
);
105 SLIST_FOREACH (ipcht_entry
, &ipcht_list
, sht_next
)
107 if (ipcht_entry
->winpid
== proc
->winpid
)
109 ret
= proc
->p_vmspace
= &ipcht_entry
->vmspace
;
113 leave_critical_section (&ipcht_cs
);
118 ipcexit_hookthread (const LPVOID param
)
120 ipc_hookthread_storage
*shs
= (ipc_hookthread_storage
*) param
;
121 HANDLE obj
[2] = { ipcexit_event
, shs
->process_hdl
};
122 switch (WaitForMultipleObjects (2, obj
, FALSE
, INFINITE
))
125 /* Cygserver shutdown. */
127 case WAIT_OBJECT_0
+ 1:
128 /* Process exited. Call semexit_myhook to handle SEM_UNDOs for the
129 exiting process and shmexit_myhook to keep track of shared
131 if (Giant
.owner
== shs
->ipcblk
.winpid
)
133 if (support_semaphores
== TUN_TRUE
)
134 semexit_myhook (NULL
, &shs
->ipcblk
);
135 if (support_sharedmem
== TUN_TRUE
)
137 _mtx_lock (&Giant
, shs
->ipcblk
.winpid
, __FILE__
, __LINE__
);
138 ipc_p_vmspace (&shs
->ipcblk
);
139 shmexit_myhook (shs
->ipcblk
.p_vmspace
);
147 CloseHandle (shs
->process_hdl
);
148 ipc_hookthread
*ipcht_entry
, *sav_entry
;
149 enter_critical_section (&ipcht_cs
);
150 SLIST_FOREACH_SAFE (ipcht_entry
, &ipcht_list
, sht_next
, sav_entry
)
152 if (ipcht_entry
->winpid
== shs
->ipcblk
.winpid
)
154 SLIST_REMOVE (&ipcht_list
, ipcht_entry
, ipc_hookthread
, sht_next
);
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 if (wincap
.has_security ())
237 SID_IDENTIFIER_AUTHORITY nt_auth
= {SECURITY_NT_AUTHORITY
};
238 if (! AllocateAndInitializeSid (&nt_auth
, 2, 32, 544, 0, 0, 0, 0, 0, 0,
239 &admininstrator_group_sid
))
240 panic ("failed to create well known sids, error = %lu",
245 SECURITY_DESCRIPTOR sec_all_nih_sd
;
246 SECURITY_ATTRIBUTES sec_all_nih
= { sizeof (SECURITY_ATTRIBUTES
),
250 /* Global vars, determining whether the IPC stuff should be started or not. */
251 tun_bool_t support_sharedmem
= TUN_UNDEF
;
252 tun_bool_t support_msgqueues
= TUN_UNDEF
;
253 tun_bool_t support_semaphores
= TUN_UNDEF
;
258 InitializeSecurityDescriptor (&sec_all_nih_sd
, SECURITY_DESCRIPTOR_REVISION
);
259 SetSecurityDescriptorDacl (&sec_all_nih_sd
, TRUE
, 0, FALSE
);
262 mtx_init (&Giant
, "Giant", NULL
, MTX_DEF
);
264 ipcexit_event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
266 panic ("Failed to create ipcexit event object");
267 InitializeCriticalSection (&ipcht_cs
);
268 if (support_msgqueues
== TUN_TRUE
)
270 if (support_semaphores
== TUN_TRUE
)
272 if (support_sharedmem
== TUN_TRUE
)
279 ipcexit_dispose_hookthreads ();
280 CloseHandle (ipcexit_event
);
282 if (support_semaphores
== TUN_TRUE
)
284 if (support_sharedmem
== TUN_TRUE
)
286 if (support_msgqueues
== TUN_TRUE
)
288 mtx_destroy (&Giant
);
293 * Helper function to find a gid in a list of gids.
296 is_grp_member (gid_t grp
, gid_t
*grplist
, int listsize
)
299 for (; listsize
> 0; --listsize
)
300 if (grp
== grplist
[listsize
- 1])
306 * Helper function to get a specific token information from a token.
307 * This function mallocs the necessary buffer spcae by itself. It
308 * must be free'd by the calling function.
311 get_token_info (HANDLE tok
, TOKEN_INFORMATION_CLASS tic
)
316 if (!GetTokenInformation (tok
, tic
, NULL
, 0, &size
)
317 && GetLastError () != ERROR_INSUFFICIENT_BUFFER
)
319 if (!(buf
= malloc (size
)))
321 if (!GetTokenInformation (tok
, tic
, buf
, size
, &size
))
330 * Check if client user helds "mode" permission when accessing object
331 * associated with "perm" permission record.
332 * Returns an error code.
335 ipcperm (struct thread
*td
, ipc_perm
*perm
, unsigned int mode
)
337 proc
*p
= td
->ipcblk
;
343 return (p
->uid
!= perm
->cuid
&& p
->uid
!= perm
->uid
)
346 if (p
->uid
!= perm
->cuid
&& p
->uid
!= perm
->uid
)
348 /* If the user is a member of the creator or owner group, test
349 against group bits, otherwise against other bits. */
350 mode
>>= p
->gid
!= perm
->gid
&& p
->gid
!= perm
->cgid
351 && !is_grp_member (perm
->gid
, p
->gidlist
, p
->gidcnt
)
352 && !is_grp_member (perm
->cgid
, p
->gidlist
, p
->gidcnt
)
355 return (mode
& perm
->mode
) != mode
? EACCES
: 0;
359 * Check for client user being superuser.
360 * Returns an error code.
363 suser (struct thread
*td
)
365 /* Always superuser on 9x. */
366 if (!wincap
.has_security ())
369 /* This value has been set at ImpersonateNamedPipeClient() time
370 using the token information. See adjust_identity_info() below. */
371 return td
->ipcblk
->is_admin
? 0 : EACCES
;
375 * Retrieves user and group info from impersonated token and creates the
376 * correct uid, gid, gidlist and is_admin entries in p from that.
379 adjust_identity_info (struct proc
*p
)
383 /* No access tokens on 9x. */
384 if (!wincap
.has_security ())
387 if (!OpenThreadToken (GetCurrentThread (), TOKEN_READ
, TRUE
, &tok
))
389 debug ("Failed to open worker thread access token for pid %d, winpid %d",
390 p
->cygpid
, p
->winpid
);
394 /* Get uid from user SID in token. */
396 if (!(user
= (PTOKEN_USER
)get_token_info (tok
, TokenUser
)))
398 p
->uid
= cygwin_internal (CW_GET_UID_FROM_SID
, user
->User
.Sid
);
400 if (p
->uid
== (uid_t
)-1)
401 log (LOG_WARNING
, "WARNING: User not found in /etc/passwd! Using uid -1!");
403 /* Get gid from primary group SID in token. */
404 PTOKEN_PRIMARY_GROUP pgrp
;
405 if (!(pgrp
= (PTOKEN_PRIMARY_GROUP
)get_token_info (tok
, TokenPrimaryGroup
)))
407 p
->gid
= cygwin_internal (CW_GET_GID_FROM_SID
, pgrp
->PrimaryGroup
);
409 if (p
->gid
== (gid_t
)-1)
410 log (LOG_WARNING
,"WARNING: Group not found in /etc/passwd! Using gid -1!");
412 /* Generate gid list from token group's SID list. Also look if the token
413 has an enabled admin group SID. That means, the process has admin
414 privileges. That knowledge is used in suser(). */
416 if (!(gsids
= (PTOKEN_GROUPS
)get_token_info (tok
, TokenGroups
)))
418 if (gsids
->GroupCount
)
420 p
->gidlist
= (gid_t
*) calloc (gsids
->GroupCount
, sizeof (gid_t
));
422 p
->gidcnt
= gsids
->GroupCount
;
424 for (DWORD i
= 0; i
< gsids
->GroupCount
; ++i
)
427 p
->gidlist
[i
] = cygwin_internal (CW_GET_GID_FROM_SID
,
428 gsids
->Groups
[i
].Sid
);
429 if (EqualSid (gsids
->Groups
[i
].Sid
, admininstrator_group_sid
)
430 && (gsids
->Groups
[i
].Attributes
& SE_GROUP_ENABLED
))
440 log (LOG_CRIT
, "Failed to get token information for pid %d, winpid %d",
441 p
->cygpid
, p
->winpid
);
446 * Windows wrapper implementation of the VM functions called by sysv_shm.cc.
450 _vm_pager_allocate (int size
, int shmflg
)
452 /* Create the file mapping object with full access for everyone. This is
453 necessary to allow later calls to shmctl(..., IPC_SET,...) to
454 change the access rights and ownership of a shared memory region.
455 The access rights are tested at the beginning of every shm... function.
456 Note that this does not influence the actual read or write access
457 defined in a call to shmat. */
458 vm_object_t object
= CreateFileMapping (INVALID_HANDLE_VALUE
, &sec_all_nih
,
459 PAGE_READWRITE
, 0, size
, NULL
);
461 panic ("CreateFileMapping in _vm_pager_allocate failed, %E");
466 vm_object_duplicate (struct thread
*td
, vm_object_t object
)
468 vm_object_t dup_object
;
469 if (!DuplicateHandle (GetCurrentProcess (), object
,
470 td
->client
->handle (), &dup_object
,
471 0, TRUE
, DUPLICATE_SAME_ACCESS
))
472 panic ("!DuplicateHandle in vm_object_duplicate failed, %E");
477 vm_object_deallocate (vm_object_t object
)
480 CloseHandle (object
);
484 * Tunable parameters are read from a system wide cygserver.conf file.
485 * On the first call to tunable_int_fetch, the file is read and the
486 * parameters are set accordingly. Each parameter has default, max and
490 enum tun_params_type
{
503 tun_params_type type
;
504 union tun_value value
;
507 void (*check_func
)(tun_struct
*, char *, const char *);
511 default_tun_check (tun_struct
*that
, char *value
, const char *fname
)
518 val
.ival
= strtoul (value
, &c
, 10);
519 if (!val
.ival
|| (c
&& *c
))
520 panic ("Error in config file %s: Value of parameter %s malformed",
522 if (val
.ival
< that
->min
.ival
|| val
.ival
> that
->max
.ival
)
523 panic ("Error in config file %s: Value of parameter %s must be "
524 "between %lu and %lu",
525 fname
, that
->name
, that
->min
.ival
, that
->max
.ival
);
526 if (that
->value
.ival
)
527 panic ("Error in config file %s: Parameter %s set twice.\n",
529 that
->value
.ival
= val
.ival
;
532 if (!strcasecmp (value
, "no") || !strcasecmp (value
, "n")
533 || !strcasecmp (value
, "false") || !strcasecmp (value
, "f")
534 || !strcasecmp (value
, "0"))
535 val
.bval
= TUN_FALSE
;
536 else if (!strcasecmp (value
, "yes") || !strcasecmp (value
, "y")
537 || !strcasecmp (value
, "true") || !strcasecmp (value
, "t")
538 || !strcasecmp (value
, "1"))
541 panic ("Error in config file %s: Value of parameter %s malformed\n"
542 "Allowed values: \"yes\", \"no\", \"y\", \"n\", \"true\", \"false\", \"t\", \"f\", \"1\" and \"0\"", fname
, that
->name
);
543 that
->value
.bval
= val
.bval
;
546 /* Shouldn't happen. */
547 panic ("Internal error: Wrong type of tunable parameter");
552 static tun_struct tunable_params
[] =
555 { "kern.srv.cleanup_threads", TUN_INT
, {0}, {1}, {16}, default_tun_check
},
556 { "kern.srv.request_threads", TUN_INT
, {0}, {1}, {64}, default_tun_check
},
557 { "kern.srv.sharedmem", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
558 { "kern.srv.msgqueues", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
559 { "kern.srv.semaphores", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
562 { "kern.log.syslog", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
563 { "kern.log.stderr", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
564 { "kern.log.debug", TUN_BOOL
, {TUN_UNDEF
}, {TUN_FALSE
}, {TUN_TRUE
}, default_tun_check
},
565 { "kern.log.level", TUN_INT
, {0}, {1}, {7}, default_tun_check
},
568 { "kern.ipc.msgseg", TUN_INT
, {0}, {256}, {65535}, default_tun_check
},
569 { "kern.ipc.msgssz", TUN_INT
, {0}, {8}, {1024}, default_tun_check
},
570 { "kern.ipc.msgmnb", TUN_INT
, {0}, {1}, {65535}, default_tun_check
},
571 { "kern.ipc.msgmni", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
572 { "kern.ipc.msgtql", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
575 //{ "kern.ipc.semmap", TUN_INT, {0}, {1}, {1024}, default_tun_check},
576 { "kern.ipc.semmni", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
577 { "kern.ipc.semmns", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
578 { "kern.ipc.semmnu", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
579 { "kern.ipc.semmsl", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
580 { "kern.ipc.semopm", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
581 { "kern.ipc.semume", TUN_INT
, {0}, {1}, {1024}, default_tun_check
},
582 //{ "kern.ipc.semusz", TUN_INT, {0}, {1}, {1024}, default_tun_check},
583 { "kern.ipc.semvmx", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
584 { "kern.ipc.semaem", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
587 { "kern.ipc.shmmaxpgs", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
588 //{ "kern.ipc.shmmin", TUN_INT, {0}, {1}, {32767}, default_tun_check},
589 { "kern.ipc.shmmni", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
590 { "kern.ipc.shmseg", TUN_INT
, {0}, {1}, {32767}, default_tun_check
},
591 //{ "kern.ipc.shm_use_phys", TUN_INT, {0}, {1}, {32767}, default_tun_check},
592 { NULL
, TUN_NULL
, {0}, {0}, {0}, NULL
}
595 #define skip_whitespace(c) while (*(c) && isspace (*(c))) ++(c)
596 #define skip_nonwhitespace(c) while (*(c) && !isspace (*(c)) && *(c) != '#') ++(c)
597 #define end_of_content(c) (!*(c) || *(c) == '#')
600 tunable_param_init (const char *config_file
, bool force
)
602 FILE *fp
= fopen (config_file
, "rt");
606 panic ("can't open config file %s\n", config_file
);
610 while (fgets (line
, 1024, fp
))
612 char *c
= strrchr (line
, '\n');
614 panic ("Line too long in confg file %s\n", config_file
);
615 /* Overwrite trailing NL. */
619 if (end_of_content (c
))
621 /* So we are on the first character of a parameter name. */
623 /* Find end of name. */
624 skip_nonwhitespace (c
);
625 if (end_of_content (c
))
628 panic ("Error in config file %s: Parameter %s has no value.\n",
631 /* Mark end of name. */
634 if (end_of_content (c
))
635 panic ("Error in config file %s: Parameter %s has no value.\n",
637 /* Now we are on the first character of a parameter's value. */
639 /* This only works for simple parameters. If complex string parameters
640 are added at one point, the scanning routine must be changed here. */
641 /* Find end of value. */
642 skip_nonwhitespace (c
);
643 /* Mark end of value. */
645 /* Now look if name is one from our list. */
647 for (s
= &tunable_params
[0]; s
->name
; ++s
)
648 if (!strcmp (name
, s
->name
))
650 /* Now read value and check for validity. check_func doesn't
652 s
->check_func (s
, value
, config_file
);
656 panic ("Error in config file %s: Unknown parameter %s.\n",
663 tunable_int_fetch (const char *name
, long *tunable_target
)
666 for (s
= &tunable_params
[0]; s
->name
; ++s
)
667 if (!strcmp (name
, s
->name
))
669 if (!s
) /* Not found */
671 if (s
->type
!= TUN_INT
) /* Wrong type */
673 if (!s
->value
.ival
) /* Not set in config file */
675 *tunable_target
= s
->value
.ival
;
676 debug ("\nSet %s to %lu\n", name
, *tunable_target
);
680 tunable_bool_fetch (const char *name
, tun_bool_t
*tunable_target
)
683 const char *tun_bool_val_string
[] = { "undefined", "no", "yes" };
684 for (s
= &tunable_params
[0]; s
->name
; ++s
)
685 if (!strcmp (name
, s
->name
))
687 if (!s
) /* Not found */
689 if (s
->type
!= TUN_BOOL
) /* Wrong type */
691 if (!s
->value
.ival
) /* Not set in config file */
693 *tunable_target
= s
->value
.bval
;
694 debug ("\nSet %s to %s\n", name
, tun_bool_val_string
[*tunable_target
]);
696 #endif /* __OUTSIDE_CYGWIN__ */