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 if (sid_id_auth (sid
) == 5 /* SECURITY_NT_AUTHORITY */
432 && sid_sub_auth (sid
, 0) == SECURITY_BUILTIN_DOMAIN_RID
433 && cygheap
->dom
.member_machine ()
434 && nss_db_enum_primary ())
436 fetch_user_arg_t arg
;
439 char *line
= pg
.fetch_account_from_windows (arg
);
441 return pg
.add_account_post_fetch (line
, false);
448 gr_ent::getgrent (void)
450 if (state
== rewound
)
454 return (struct group
*) getent ();
463 extern "C" struct group
*
466 return grent
.getgrent ();
475 /* *_filtered functions are called from mkgroup */
477 setgrent_filtered (int enums
, PCWSTR enum_tdoms
)
479 gr_ent
*gr
= new gr_ent
;
481 gr
->setgrent (enums
, enum_tdoms
);
486 getgrent_filtered (void *gr
)
488 return (void *) ((gr_ent
*) gr
)->getgrent ();
492 endgrent_filtered (void *gr
)
494 ((gr_ent
*) gr
)->endgrent ();
498 internal_getgroups (int gidsetsize
, gid_t
*grouplist
, cyg_ldap
*pldap
)
503 PTOKEN_GROUPS groups
;
506 PLSA_REFERENCED_DOMAIN_LIST dlst
= NULL
;
507 PLSA_TRANSLATED_NAME nlst
= NULL
;
513 if (cygheap
->user
.groups
.issetgroups ())
515 for (int pg
= 0; pg
< cygheap
->user
.groups
.sgsids
.count (); ++pg
)
516 if ((grp
= internal_getgrsid (cygheap
->user
.groups
.sgsids
.sids
[pg
],
519 if (cnt
< gidsetsize
)
520 grouplist
[cnt
] = grp
->gr_gid
;
522 if (gidsetsize
&& cnt
> gidsetsize
)
531 /* If impersonated, use impersonation token. */
532 tok
= cygheap
->user
.issetuid () ? cygheap
->user
.primary_token ()
535 /* Fetch groups from user token. */
536 groups
= (PTOKEN_GROUPS
) tp
.w_get ();
537 status
= NtQueryInformationToken (tok
, TokenGroups
, groups
, 2 * NT_MAX_PATH
,
539 if (!NT_SUCCESS (status
))
541 debug_printf ("NtQueryInformationToken(TokenGroups) %y", status
);
544 /* Iterate over the group list and check which of them are already cached.
545 Those are simply copied to grouplist. The non-cached ones are collected
546 in sidp_buf for a later call to LsaLookupSids. */
547 sidp_buf
= (PSID
*) tp
.w_get ();
549 for (DWORD pg
= 0; pg
< groups
->GroupCount
; ++pg
)
551 cygpsid sid
= groups
->Groups
[pg
].Sid
;
552 if ((groups
->Groups
[pg
].Attributes
553 & (SE_GROUP_ENABLED
| SE_GROUP_INTEGRITY_ENABLED
)) == 0
554 || sid
== well_known_world_sid
)
556 if ((grp
= internal_getgrsid_cachedonly (sid
)))
558 if (cnt
< gidsetsize
)
559 grouplist
[cnt
] = grp
->gr_gid
;
561 if (gidsetsize
&& cnt
> gidsetsize
)
568 sidp_buf
[scnt
++] = sid
;
570 /* If there are non-cached groups left, try to fetch them. */
573 /* Don't call LsaLookupSids if we're not utilizing the Windows account
574 DBs. If we don't have access to the AD, which is one good reason to
575 disable passwd/group: db in nsswitch.conf, then the subsequent call
576 to LsaLookupSids will take 5 - 10 seconds in some environments. */
577 if (!cygheap
->pg
.nss_grp_db ())
579 for (DWORD pg
= 0; pg
< scnt
; ++pg
)
581 cygpsid sid
= sidp_buf
[pg
];
582 if ((grp
= internal_getgrsid (sid
, NULL
)))
584 if (cnt
< gidsetsize
)
585 grouplist
[cnt
] = grp
->gr_gid
;
587 if (gidsetsize
&& cnt
> gidsetsize
)
596 /* Otherwise call LsaLookupSids and call internal_getgrfull on the
597 returned groups. This performs a lot better than calling
598 internal_getgrsid on each group. */
599 status
= STATUS_ACCESS_DENIED
;
600 HANDLE lsa
= lsa_open_policy (NULL
, POLICY_LOOKUP_NAMES
);
603 debug_printf ("POLICY_LOOKUP_NAMES right not given?");
606 status
= LsaLookupSids (lsa
, scnt
, sidp_buf
, &dlst
, &nlst
);
607 lsa_close_policy (lsa
);
608 if (NT_SUCCESS (status
))
610 for (ULONG ncnt
= 0; ncnt
< scnt
; ++ncnt
)
612 static UNICODE_STRING empty
= { 0, 0, (PWSTR
) L
"" };
613 fetch_acc_t full_acc
=
615 .sid
= sidp_buf
[ncnt
],
616 .name
= &nlst
[ncnt
].Name
,
618 .acc_type
= nlst
[ncnt
].Use
621 if (nlst
[ncnt
].DomainIndex
>= 0)
622 full_acc
.dom
= &dlst
->Domains
[nlst
[ncnt
].DomainIndex
].Name
;
623 if ((grp
= internal_getgrfull (full_acc
, pldap
)))
625 if (cnt
< gidsetsize
)
626 grouplist
[cnt
] = grp
->gr_gid
;
628 if (gidsetsize
&& cnt
> gidsetsize
)
640 LsaFreeMemory (dlst
);
642 LsaFreeMemory (nlst
);
649 getgroups (int gidsetsize
, gid_t
*grouplist
)
653 return internal_getgroups (gidsetsize
, grouplist
, &cldap
);
656 /* Core functionality of initgroups and getgrouplist. */
658 get_groups (const char *user
, gid_t gid
, cygsidlist
&gsids
)
662 cygheap
->user
.deimpersonate ();
663 struct passwd
*pw
= internal_getpwnam (user
, &cldap
);
664 struct group
*grp
= internal_getgrgid (gid
, &cldap
);
665 cygsid usersid
, grpsid
;
666 if (usersid
.getfrompw (pw
))
667 get_server_groups (gsids
, usersid
, NO_CHK_DISABLED
);
668 if (gid
!= ILLEGAL_GID
&& grpsid
.getfromgr (grp
))
670 cygheap
->user
.reimpersonate ();
674 initgroups (const char *user
, gid_t gid
)
676 assert (user
!= NULL
);
677 cygsidlist
tmp_gsids (cygsidlist_auto
, 12);
678 get_groups (user
, gid
, tmp_gsids
);
679 cygsidlist
new_gsids (cygsidlist_alloc
, tmp_gsids
.count ());
680 for (int i
= 0; i
< tmp_gsids
.count (); i
++)
681 new_gsids
.sids
[i
] = tmp_gsids
.sids
[i
];
682 new_gsids
.count (tmp_gsids
.count ());
683 cygheap
->user
.groups
.update_supp (new_gsids
);
684 syscall_printf ( "0 = initgroups(%s, %u)", user
, gid
);
689 getgrouplist (const char *user
, gid_t gid
, gid_t
*groups
, int *ngroups
)
696 /* Note that it's not defined if groups or ngroups may be NULL!
697 GLibc does not check the pointers on entry and just uses them.
698 FreeBSD calls assert for ngroups and allows a NULL groups if
699 *ngroups is 0. We follow FreeBSD's lead here, but always allow
700 a NULL groups pointer. */
701 assert (user
!= NULL
);
702 assert (ngroups
!= NULL
);
704 cygsidlist
tmp_gsids (cygsidlist_auto
, 12);
705 get_groups (user
, gid
, tmp_gsids
);
706 for (int i
= 0; i
< tmp_gsids
.count (); i
++)
707 if ((grp
= internal_getgrsid (tmp_gsids
.sids
[i
], &cldap
)) != NULL
)
709 if (groups
&& cnt
< *ngroups
)
710 groups
[cnt
] = grp
->gr_gid
;
719 syscall_printf ( "%d = getgrouplist(%s, %u, %p, %d)",
720 ret
, user
, gid
, groups
, *ngroups
);
724 /* setgroups: standards? */
726 setgroups (int ngroups
, const gid_t
*grouplist
)
728 syscall_printf ("setgroups (%d)", ngroups
);
729 if (ngroups
< 0 || (ngroups
> 0 && !grouplist
))
735 cygsidlist
gsids (cygsidlist_alloc
, ngroups
);
739 if (ngroups
&& !gsids
.sids
)
742 for (int gidx
= 0; gidx
< ngroups
; ++gidx
)
744 if ((grp
= internal_getgrgid (grouplist
[gidx
], &cldap
))
745 && gsids
.addfromgr (grp
))
747 debug_printf ("No sid found for gid %u", grouplist
[gidx
]);
752 cygheap
->user
.groups
.update_supp (gsids
);