3 Original stubs by Jason Molenda of Cygnus Support, crash@cygnus.com
4 First implementation by Gunther Ebert, gunther.ebert@ixos-leipzig.de
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
25 #include "miscfuncs.h"
29 static char * NO_COPY_RO null_ptr
;
32 pwdgrp::parse_group ()
34 pg_grp
&grp
= group ()[curr_lines
];
35 grp
.g
.gr_name
= next_str (':');
38 grp
.g
.gr_passwd
= next_str (':');
39 /* Note that lptr points to the first byte of the gr_gid field.
40 We deliberately ignore the gr_gid and gr_mem entries when copying
41 the buffer content since they are not referenced anymore. */
42 grp
.len
= lptr
- grp
.g
.gr_name
;
43 if (!next_num (grp
.g
.gr_gid
))
45 /* Don't generate gr_mem entries. */
46 grp
.g
.gr_mem
= &null_ptr
;
48 if (csid
.getfromgr_passwd (&grp
.g
))
49 RtlCopySid (SECURITY_MAX_SID_SIZE
, grp
.sid
, csid
);
53 muto NO_COPY
pwdgrp::pglock
;
58 pwdgrp_buf_elem_size
= sizeof (pg_grp
);
59 parse
= &pwdgrp::parse_group
;
63 pwdgrp::find_group (cygpsid
&sid
)
65 for (ULONG i
= 0; i
< curr_lines
; i
++)
66 if (sid
== group ()[i
].sid
)
67 return &group ()[i
].g
;
72 pwdgrp::find_group (const char *name
)
74 for (ULONG i
= 0; i
< curr_lines
; i
++)
75 if (strcasematch (group ()[i
].g
.gr_name
, name
))
76 return &group ()[i
].g
;
81 pwdgrp::find_group (gid_t gid
)
83 for (ULONG i
= 0; i
< curr_lines
; i
++)
84 if (gid
== group ()[i
].g
.gr_gid
)
85 return &group ()[i
].g
;
90 internal_getgrsid (cygpsid
&sid
, cyg_ldap
*pldap
)
94 cygheap
->pg
.nss_init ();
95 /* Check caches first. */
96 if (cygheap
->pg
.nss_cygserver_caching ()
97 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.find_group (sid
)))
99 if (cygheap
->pg
.nss_grp_files ()
100 && (ret
= cygheap
->pg
.grp_cache
.file
.find_group (sid
)))
102 if (cygheap
->pg
.nss_grp_db ()
103 && (ret
= cygheap
->pg
.grp_cache
.win
.find_group (sid
)))
105 /* Ask sources afterwards. */
106 if (cygheap
->pg
.nss_cygserver_caching ()
107 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.add_group_from_cygserver (sid
)))
109 if (cygheap
->pg
.nss_grp_files ())
111 cygheap
->pg
.grp_cache
.file
.check_file ();
112 if ((ret
= cygheap
->pg
.grp_cache
.file
.add_group_from_file (sid
)))
115 if (cygheap
->pg
.nss_grp_db ())
116 return cygheap
->pg
.grp_cache
.win
.add_group_from_windows (sid
, pldap
);
120 /* Like internal_getgrsid but return only already cached data,
122 static struct group
*
123 internal_getgrsid_cachedonly (cygpsid
&sid
)
127 /* Check caches only. */
128 if (cygheap
->pg
.nss_cygserver_caching ()
129 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.find_group (sid
)))
131 if (cygheap
->pg
.nss_grp_files ()
132 && (ret
= cygheap
->pg
.grp_cache
.file
.find_group (sid
)))
134 if (cygheap
->pg
.nss_grp_db ()
135 && (ret
= cygheap
->pg
.grp_cache
.win
.find_group (sid
)))
140 /* Called from internal_getgroups. The full information required to create
141 a group account entry is already available from the LookupAccountSids
142 call. internal_getgrfull passes all available info into
143 pwdgrp::fetch_account_from_line, thus avoiding a LookupAccountSid call
144 for each group. This is quite a bit faster, especially in slower
146 static struct group
* __attribute__((used
))
147 internal_getgrfull (fetch_acc_t
&full_acc
, cyg_ldap
*pldap
)
151 cygheap
->pg
.nss_init ();
152 /* Skip local caches, internal_getgroups already called
153 internal_getgrsid_cachedonly. */
154 if (cygheap
->pg
.nss_cygserver_caching ()
155 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.add_group_from_cygserver
158 if (cygheap
->pg
.nss_grp_files ())
160 cygheap
->pg
.grp_cache
.file
.check_file ();
161 if ((ret
= cygheap
->pg
.grp_cache
.file
.add_group_from_file
165 if (cygheap
->pg
.nss_grp_db ())
166 return cygheap
->pg
.grp_cache
.win
.add_group_from_windows (full_acc
, pldap
);
170 /* This function gets only called from mkgroup via cygwin_internal. */
172 internal_getgrsid_from_db (cygpsid
&sid
)
174 cygheap
->pg
.nss_init ();
175 return cygheap
->pg
.grp_cache
.win
.add_group_from_windows (sid
);
179 internal_getgrnam (const char *name
, cyg_ldap
*pldap
)
183 cygheap
->pg
.nss_init ();
184 /* Check caches first. */
185 if (cygheap
->pg
.nss_cygserver_caching ()
186 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.find_group (name
)))
188 if (cygheap
->pg
.nss_grp_files ()
189 && (ret
= cygheap
->pg
.grp_cache
.file
.find_group (name
)))
191 if (cygheap
->pg
.nss_grp_db ()
192 && (ret
= cygheap
->pg
.grp_cache
.win
.find_group (name
)))
194 /* Ask sources afterwards. */
195 if (cygheap
->pg
.nss_cygserver_caching ()
196 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.add_group_from_cygserver (name
)))
198 if (cygheap
->pg
.nss_grp_files ())
200 cygheap
->pg
.grp_cache
.file
.check_file ();
201 if ((ret
= cygheap
->pg
.grp_cache
.file
.add_group_from_file (name
)))
204 if (cygheap
->pg
.nss_grp_db ())
205 return cygheap
->pg
.grp_cache
.win
.add_group_from_windows (name
, pldap
);
210 internal_getgrgid (gid_t gid
, cyg_ldap
*pldap
)
214 cygheap
->pg
.nss_init ();
215 /* Check caches first. */
216 if (cygheap
->pg
.nss_cygserver_caching ()
217 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.find_group (gid
)))
219 if (cygheap
->pg
.nss_grp_files ()
220 && (ret
= cygheap
->pg
.grp_cache
.file
.find_group (gid
)))
222 if (cygheap
->pg
.nss_grp_db ()
223 && (ret
= cygheap
->pg
.grp_cache
.win
.find_group (gid
)))
225 /* Ask sources afterwards. */
226 if (cygheap
->pg
.nss_cygserver_caching ()
227 && (ret
= cygheap
->pg
.grp_cache
.cygserver
.add_group_from_cygserver (gid
)))
229 if (cygheap
->pg
.nss_grp_files ())
231 cygheap
->pg
.grp_cache
.file
.check_file ();
232 if ((ret
= cygheap
->pg
.grp_cache
.file
.add_group_from_file (gid
)))
235 if (cygheap
->pg
.nss_grp_db () || gid
== ILLEGAL_GID
)
236 return cygheap
->pg
.grp_cache
.win
.add_group_from_windows (gid
, pldap
);
241 getgrgid_r (gid_t gid
, struct group
*grp
, char *buffer
, size_t bufsize
,
242 struct group
**result
)
249 struct group
*tempgr
= internal_getgrgid (gid
);
250 pthread_testcancel ();
254 /* Check needed buffer size. Deliberately ignore gr_mem. */
255 size_t needsize
= strlen (tempgr
->gr_name
) + strlen (tempgr
->gr_passwd
)
256 + 2 + sizeof (char *);
257 if (needsize
> bufsize
)
260 /* Make a copy of tempgr. Deliberately ignore gr_mem. */
262 grp
->gr_gid
= tempgr
->gr_gid
;
263 buffer
= stpcpy (grp
->gr_name
= buffer
, tempgr
->gr_name
);
264 buffer
= stpcpy (grp
->gr_passwd
= buffer
+ 1, tempgr
->gr_passwd
);
265 grp
->gr_mem
= (char **) (buffer
+ 1);
266 grp
->gr_mem
[0] = NULL
;
270 /* getgrgid/getgrnam are not reentrant. */
277 static struct group
*
278 getgr_cp (struct group
*tempgr
)
282 pg_grp
*gr
= (pg_grp
*) tempgr
;
283 if (app_gr
.bufsiz
< gr
->len
)
285 char *newbuf
= (char *) realloc (app_gr
.buf
, gr
->len
);
292 app_gr
.bufsiz
= gr
->len
;
294 memcpy (app_gr
.buf
, gr
->g
.gr_name
, gr
->len
);
295 memcpy (&app_gr
.g
, &gr
->g
, sizeof gr
->g
);
296 ptrdiff_t diff
= app_gr
.buf
- gr
->g
.gr_name
;
297 app_gr
.g
.gr_name
+= diff
;
298 app_gr
.g
.gr_passwd
+= diff
;
302 extern "C" struct group
*
305 struct group
*tempgr
= internal_getgrgid (gid
);
306 pthread_testcancel ();
307 return getgr_cp (tempgr
);
311 getgrnam_r (const char *nam
, struct group
*grp
, char *buffer
,
312 size_t bufsize
, struct group
**result
)
319 struct group
*tempgr
= internal_getgrnam (nam
);
320 pthread_testcancel ();
324 /* Check needed buffer size. Deliberately ignore gr_mem. */
325 size_t needsize
= strlen (tempgr
->gr_name
) + strlen (tempgr
->gr_passwd
)
326 + 2 + sizeof (char *);
327 if (needsize
> bufsize
)
330 /* Make a copy of tempgr. Deliberately ignore gr_mem. */
332 grp
->gr_gid
= tempgr
->gr_gid
;
333 buffer
= stpcpy (grp
->gr_name
= buffer
, tempgr
->gr_name
);
334 buffer
= stpcpy (grp
->gr_passwd
= buffer
+ 1, tempgr
->gr_passwd
);
335 grp
->gr_mem
= (char **) (buffer
+ 1);
336 grp
->gr_mem
[0] = NULL
;
340 extern "C" struct group
*
341 getgrnam (const char *name
)
343 struct group
*tempgr
= internal_getgrnam (name
);
344 pthread_testcancel ();
345 return getgr_cp (tempgr
);
348 /* getgrent functions are not reentrant. */
352 gr_ent::enumerate_caches ()
357 if (cygheap
->pg
.nss_cygserver_caching ())
359 pwdgrp
&grc
= cygheap
->pg
.grp_cache
.cygserver
;
360 if (cnt
< grc
.cached_groups ())
361 return &grc
.group ()[cnt
++].g
;
369 pwdgrp
&grf
= cygheap
->pg
.grp_cache
.file
;
371 if (cnt
< grf
.cached_groups ())
372 return &grf
.group ()[cnt
++].g
;
380 pwdgrp
&grw
= cygheap
->pg
.grp_cache
.win
;
381 if (cnt
< grw
.cached_groups ())
382 return &grw
.group ()[cnt
++].g
;
391 gr_ent::enumerate_local ()
402 NetApiBufferFree (buf
);
405 if (resume
== ULONG_MAX
)
406 ret
= ERROR_NO_MORE_ITEMS
;
408 ret
= NetLocalGroupEnum (NULL
, 0, (PBYTE
*) &buf
,
409 MAX_PREFERRED_LENGTH
,
410 &max
, &total
, &resume
);
411 if (ret
== NERR_Success
)
413 else if (ret
!= ERROR_MORE_DATA
)
415 cnt
= max
= resume
= 0;
422 DWORD slen
= SECURITY_MAX_SID_SIZE
;
423 WCHAR dom
[DNLEN
+ 1];
424 DWORD dlen
= DNLEN
+ 1;
425 SID_NAME_USE acc_type
;
427 if (!LookupAccountNameW (NULL
,
428 ((PLOCALGROUP_INFO_0
) buf
)[cnt
++].lgrpi0_name
,
429 sid
, &slen
, dom
, &dlen
, &acc_type
))
431 /* Skip builtin groups if we're enumerating AD as well to avoid
432 duplication. Don't skip "Power Users" and "Device Owners"
433 accounts, they don't show up in AD enumeration. */
434 if (cygheap
->dom
.member_machine ()
435 && nss_db_enum_primary ()
436 && sid_id_auth (sid
) == 5 /* SECURITY_NT_AUTHORITY */
437 && sid_sub_auth (sid
, 0) == SECURITY_BUILTIN_DOMAIN_RID
438 && sid_sub_auth (sid
, 1) != DOMAIN_ALIAS_RID_POWER_USERS
439 && sid_sub_auth (sid
, 1) != DOMAIN_ALIAS_RID_DEVICE_OWNERS
)
441 fetch_user_arg_t arg
;
444 char *line
= pg
.fetch_account_from_windows (arg
);
446 return pg
.add_account_post_fetch (line
, false);
453 gr_ent::getgrent (void)
455 if (state
== rewound
)
459 return (struct group
*) getent ();
468 extern "C" struct group
*
471 return grent
.getgrent ();
480 /* *_filtered functions are called from mkgroup */
482 setgrent_filtered (int enums
, PCWSTR enum_tdoms
)
484 gr_ent
*gr
= new gr_ent
;
486 gr
->setgrent (enums
, enum_tdoms
);
491 getgrent_filtered (void *gr
)
493 return (void *) ((gr_ent
*) gr
)->getgrent ();
497 endgrent_filtered (void *gr
)
499 ((gr_ent
*) gr
)->endgrent ();
503 internal_getgroups (int gidsetsize
, gid_t
*grouplist
, cyg_ldap
*pldap
)
508 PTOKEN_GROUPS groups
;
511 PLSA_REFERENCED_DOMAIN_LIST dlst
= NULL
;
512 PLSA_TRANSLATED_NAME nlst
= NULL
;
518 if (cygheap
->user
.groups
.issetgroups ())
520 for (int pg
= 0; pg
< cygheap
->user
.groups
.sgsids
.count (); ++pg
)
521 if ((grp
= internal_getgrsid (cygheap
->user
.groups
.sgsids
.sids
[pg
],
524 if (cnt
< gidsetsize
)
525 grouplist
[cnt
] = grp
->gr_gid
;
527 if (gidsetsize
&& cnt
> gidsetsize
)
536 /* If impersonated, use impersonation token. */
537 tok
= cygheap
->user
.issetuid () ? cygheap
->user
.primary_token ()
540 /* Fetch groups from user token. */
541 groups
= (PTOKEN_GROUPS
) tp
.w_get ();
542 status
= NtQueryInformationToken (tok
, TokenGroups
, groups
, 2 * NT_MAX_PATH
,
544 if (!NT_SUCCESS (status
))
546 debug_printf ("NtQueryInformationToken(TokenGroups) %y", status
);
549 /* Iterate over the group list and check which of them are already cached.
550 Those are simply copied to grouplist. The non-cached ones are collected
551 in sidp_buf for a later call to LsaLookupSids. */
552 sidp_buf
= (PSID
*) tp
.w_get ();
554 for (DWORD pg
= 0; pg
< groups
->GroupCount
; ++pg
)
556 cygpsid sid
= groups
->Groups
[pg
].Sid
;
557 if ((groups
->Groups
[pg
].Attributes
558 & (SE_GROUP_ENABLED
| SE_GROUP_INTEGRITY_ENABLED
)) == 0
559 || sid
== well_known_world_sid
)
561 if ((grp
= internal_getgrsid_cachedonly (sid
)))
563 if (cnt
< gidsetsize
)
564 grouplist
[cnt
] = grp
->gr_gid
;
566 if (gidsetsize
&& cnt
> gidsetsize
)
573 sidp_buf
[scnt
++] = sid
;
575 /* If there are non-cached groups left, try to fetch them. */
578 /* Don't call LsaLookupSids if we're not utilizing the Windows account
579 DBs. If we don't have access to the AD, which is one good reason to
580 disable passwd/group: db in nsswitch.conf, then the subsequent call
581 to LsaLookupSids will take 5 - 10 seconds in some environments. */
582 if (!cygheap
->pg
.nss_grp_db ())
584 for (DWORD pg
= 0; pg
< scnt
; ++pg
)
586 cygpsid sid
= sidp_buf
[pg
];
587 if ((grp
= internal_getgrsid (sid
, NULL
)))
589 if (cnt
< gidsetsize
)
590 grouplist
[cnt
] = grp
->gr_gid
;
592 if (gidsetsize
&& cnt
> gidsetsize
)
601 /* Otherwise call LsaLookupSids and call internal_getgrfull on the
602 returned groups. This performs a lot better than calling
603 internal_getgrsid on each group. */
604 status
= STATUS_ACCESS_DENIED
;
605 HANDLE lsa
= lsa_open_policy (NULL
, POLICY_LOOKUP_NAMES
);
608 debug_printf ("POLICY_LOOKUP_NAMES right not given?");
611 status
= LsaLookupSids (lsa
, scnt
, sidp_buf
, &dlst
, &nlst
);
612 lsa_close_policy (lsa
);
613 if (NT_SUCCESS (status
))
615 for (ULONG ncnt
= 0; ncnt
< scnt
; ++ncnt
)
617 static UNICODE_STRING empty
= { 0, 0, (PWSTR
) L
"" };
618 fetch_acc_t full_acc
=
620 .sid
= sidp_buf
[ncnt
],
621 .name
= &nlst
[ncnt
].Name
,
623 .acc_type
= nlst
[ncnt
].Use
626 if (nlst
[ncnt
].DomainIndex
>= 0)
627 full_acc
.dom
= &dlst
->Domains
[nlst
[ncnt
].DomainIndex
].Name
;
628 if ((grp
= internal_getgrfull (full_acc
, pldap
)))
630 if (cnt
< gidsetsize
)
631 grouplist
[cnt
] = grp
->gr_gid
;
633 if (gidsetsize
&& cnt
> gidsetsize
)
645 LsaFreeMemory (dlst
);
647 LsaFreeMemory (nlst
);
654 getgroups (int gidsetsize
, gid_t
*grouplist
)
658 return internal_getgroups (gidsetsize
, grouplist
, &cldap
);
661 /* Core functionality of initgroups and getgrouplist. */
663 get_groups (const char *user
, gid_t gid
, cygsidlist
&gsids
)
667 cygheap
->user
.deimpersonate ();
668 struct passwd
*pw
= internal_getpwnam (user
, &cldap
);
669 struct group
*grp
= internal_getgrgid (gid
, &cldap
);
670 cygsid usersid
, grpsid
;
671 if (usersid
.getfrompw (pw
))
672 get_server_groups (gsids
, usersid
, NO_CHK_DISABLED
);
673 if (gid
!= ILLEGAL_GID
&& grpsid
.getfromgr (grp
))
675 cygheap
->user
.reimpersonate ();
679 initgroups (const char *user
, gid_t gid
)
681 assert (user
!= NULL
);
682 cygsidlist
tmp_gsids (cygsidlist_auto
, 12);
683 get_groups (user
, gid
, tmp_gsids
);
684 cygsidlist
new_gsids (cygsidlist_alloc
, tmp_gsids
.count ());
685 for (int i
= 0; i
< tmp_gsids
.count (); i
++)
686 new_gsids
.sids
[i
] = tmp_gsids
.sids
[i
];
687 new_gsids
.count (tmp_gsids
.count ());
688 cygheap
->user
.groups
.update_supp (new_gsids
);
689 syscall_printf ( "0 = initgroups(%s, %u)", user
, gid
);
694 getgrouplist (const char *user
, gid_t gid
, gid_t
*groups
, int *ngroups
)
701 /* Note that it's not defined if groups or ngroups may be NULL!
702 GLibc does not check the pointers on entry and just uses them.
703 FreeBSD calls assert for ngroups and allows a NULL groups if
704 *ngroups is 0. We follow FreeBSD's lead here, but always allow
705 a NULL groups pointer. */
706 assert (user
!= NULL
);
707 assert (ngroups
!= NULL
);
709 cygsidlist
tmp_gsids (cygsidlist_auto
, 12);
710 get_groups (user
, gid
, tmp_gsids
);
711 for (int i
= 0; i
< tmp_gsids
.count (); i
++)
712 if ((grp
= internal_getgrsid (tmp_gsids
.sids
[i
], &cldap
)) != NULL
)
714 if (groups
&& cnt
< *ngroups
)
715 groups
[cnt
] = grp
->gr_gid
;
724 syscall_printf ( "%d = getgrouplist(%s, %u, %p, %d)",
725 ret
, user
, gid
, groups
, *ngroups
);
729 /* setgroups: standards? */
731 setgroups (int ngroups
, const gid_t
*grouplist
)
733 syscall_printf ("setgroups (%d)", ngroups
);
734 if (ngroups
< 0 || (ngroups
> 0 && !grouplist
))
740 cygsidlist
gsids (cygsidlist_alloc
, ngroups
);
744 if (ngroups
&& !gsids
.sids
)
747 for (int gidx
= 0; gidx
< ngroups
; ++gidx
)
749 if ((grp
= internal_getgrgid (grouplist
[gidx
], &cldap
))
750 && gsids
.addfromgr (grp
))
752 debug_printf ("No sid found for gid %u", grouplist
[gidx
]);
757 cygheap
->user
.groups
.update_supp (gsids
);