Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / grp.cc
blob77cf6a72c69f8bebc88fe34319b4319cecae9d7d
1 /* grp.cc
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
10 details. */
12 #include "winsup.h"
13 #include <lm.h>
14 #include <ntsecapi.h>
15 #include <assert.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include "cygerrno.h"
19 #include "pinfo.h"
20 #include "path.h"
21 #include "fhandler.h"
22 #include "dtable.h"
23 #include "cygheap.h"
24 #include "ntdll.h"
25 #include "miscfuncs.h"
26 #include "ldap.h"
27 #include "tls_pbuf.h"
29 static char * NO_COPY_RO null_ptr;
31 bool
32 pwdgrp::parse_group ()
34 pg_grp &grp = group ()[curr_lines];
35 grp.g.gr_name = next_str (':');
36 if (!*grp.g.gr_name)
37 return false;
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))
44 return false;
45 /* Don't generate gr_mem entries. */
46 grp.g.gr_mem = &null_ptr;
47 cygsid csid;
48 if (csid.getfromgr_passwd (&grp.g))
49 RtlCopySid (SECURITY_MAX_SID_SIZE, grp.sid, csid);
50 return true;
53 muto NO_COPY pwdgrp::pglock;
55 void
56 pwdgrp::init_grp ()
58 pwdgrp_buf_elem_size = sizeof (pg_grp);
59 parse = &pwdgrp::parse_group;
62 struct 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;
68 return NULL;
71 struct group *
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;
77 return NULL;
80 struct group *
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;
86 return NULL;
89 struct group *
90 internal_getgrsid (cygpsid &sid, cyg_ldap *pldap)
92 struct group *ret;
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)))
98 return ret;
99 if (cygheap->pg.nss_grp_files ()
100 && (ret = cygheap->pg.grp_cache.file.find_group (sid)))
101 return ret;
102 if (cygheap->pg.nss_grp_db ()
103 && (ret = cygheap->pg.grp_cache.win.find_group (sid)))
104 return ret;
105 /* Ask sources afterwards. */
106 if (cygheap->pg.nss_cygserver_caching ()
107 && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver (sid)))
108 return ret;
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)))
113 return ret;
115 if (cygheap->pg.nss_grp_db ())
116 return cygheap->pg.grp_cache.win.add_group_from_windows (sid, pldap);
117 return NULL;
120 /* Like internal_getgrsid but return only already cached data,
121 NULL otherwise. */
122 static struct group *
123 internal_getgrsid_cachedonly (cygpsid &sid)
125 struct group *ret;
127 /* Check caches only. */
128 if (cygheap->pg.nss_cygserver_caching ()
129 && (ret = cygheap->pg.grp_cache.cygserver.find_group (sid)))
130 return ret;
131 if (cygheap->pg.nss_grp_files ()
132 && (ret = cygheap->pg.grp_cache.file.find_group (sid)))
133 return ret;
134 if (cygheap->pg.nss_grp_db ()
135 && (ret = cygheap->pg.grp_cache.win.find_group (sid)))
136 return ret;
137 return NULL;
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
145 environments. */
146 static struct group * __attribute__((used))
147 internal_getgrfull (fetch_acc_t &full_acc, cyg_ldap *pldap)
149 struct group *ret;
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
156 (full_acc.sid)))
157 return ret;
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
162 (full_acc.sid)))
163 return ret;
165 if (cygheap->pg.nss_grp_db ())
166 return cygheap->pg.grp_cache.win.add_group_from_windows (full_acc, pldap);
167 return NULL;
170 /* This function gets only called from mkgroup via cygwin_internal. */
171 struct group *
172 internal_getgrsid_from_db (cygpsid &sid)
174 cygheap->pg.nss_init ();
175 return cygheap->pg.grp_cache.win.add_group_from_windows (sid);
178 struct group *
179 internal_getgrnam (const char *name, cyg_ldap *pldap)
181 struct group *ret;
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)))
187 return ret;
188 if (cygheap->pg.nss_grp_files ()
189 && (ret = cygheap->pg.grp_cache.file.find_group (name)))
190 return ret;
191 if (cygheap->pg.nss_grp_db ()
192 && (ret = cygheap->pg.grp_cache.win.find_group (name)))
193 return ret;
194 /* Ask sources afterwards. */
195 if (cygheap->pg.nss_cygserver_caching ()
196 && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver (name)))
197 return ret;
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)))
202 return ret;
204 if (cygheap->pg.nss_grp_db ())
205 return cygheap->pg.grp_cache.win.add_group_from_windows (name, pldap);
206 return NULL;
209 struct group *
210 internal_getgrgid (gid_t gid, cyg_ldap *pldap)
212 struct group *ret;
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)))
218 return ret;
219 if (cygheap->pg.nss_grp_files ()
220 && (ret = cygheap->pg.grp_cache.file.find_group (gid)))
221 return ret;
222 if (cygheap->pg.nss_grp_db ()
223 && (ret = cygheap->pg.grp_cache.win.find_group (gid)))
224 return ret;
225 /* Ask sources afterwards. */
226 if (cygheap->pg.nss_cygserver_caching ()
227 && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver (gid)))
228 return ret;
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)))
233 return ret;
235 if (cygheap->pg.nss_grp_db () || gid == ILLEGAL_GID)
236 return cygheap->pg.grp_cache.win.add_group_from_windows (gid, pldap);
237 return NULL;
240 extern "C" int
241 getgrgid_r (gid_t gid, struct group *grp, char *buffer, size_t bufsize,
242 struct group **result)
244 *result = NULL;
246 if (!grp || !buffer)
247 return ERANGE;
249 struct group *tempgr = internal_getgrgid (gid);
250 pthread_testcancel ();
251 if (!tempgr)
252 return 0;
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)
258 return ERANGE;
260 /* Make a copy of tempgr. Deliberately ignore gr_mem. */
261 *result = grp;
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;
267 return 0;
270 /* getgrgid/getgrnam are not reentrant. */
271 static struct {
272 struct group g;
273 char *buf;
274 size_t bufsiz;
275 } app_gr;
277 static struct group *
278 getgr_cp (struct group *tempgr)
280 if (!tempgr)
281 return NULL;
282 pg_grp *gr = (pg_grp *) tempgr;
283 if (app_gr.bufsiz < gr->len)
285 char *newbuf = (char *) realloc (app_gr.buf, gr->len);
286 if (!newbuf)
288 set_errno (ENOMEM);
289 return NULL;
291 app_gr.buf = newbuf;
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;
299 return &app_gr.g;
302 extern "C" struct group *
303 getgrgid (gid_t gid)
305 struct group *tempgr = internal_getgrgid (gid);
306 pthread_testcancel ();
307 return getgr_cp (tempgr);
310 extern "C" int
311 getgrnam_r (const char *nam, struct group *grp, char *buffer,
312 size_t bufsize, struct group **result)
314 *result = NULL;
316 if (!grp || !buffer)
317 return ERANGE;
319 struct group *tempgr = internal_getgrnam (nam);
320 pthread_testcancel ();
321 if (!tempgr)
322 return 0;
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)
328 return ERANGE;
330 /* Make a copy of tempgr. Deliberately ignore gr_mem. */
331 *result = grp;
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;
337 return 0;
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. */
349 static gr_ent grent;
351 void *
352 gr_ent::enumerate_caches ()
354 switch (max)
356 case 0:
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;
363 cnt = 0;
364 max = 1;
365 fallthrough;
366 case 1:
367 if (from_files)
369 pwdgrp &grf = cygheap->pg.grp_cache.file;
370 grf.check_file ();
371 if (cnt < grf.cached_groups ())
372 return &grf.group ()[cnt++].g;
374 cnt = 0;
375 max = 2;
376 fallthrough;
377 case 2:
378 if (from_db)
380 pwdgrp &grw = cygheap->pg.grp_cache.win;
381 if (cnt < grw.cached_groups ())
382 return &grw.group ()[cnt++].g;
384 break;
386 cnt = max = 0;
387 return NULL;
390 void *
391 gr_ent::enumerate_local ()
393 while (true)
395 if (!cnt)
397 DWORD total;
398 NET_API_STATUS ret;
400 if (buf)
402 NetApiBufferFree (buf);
403 buf = NULL;
405 if (resume == ULONG_MAX)
406 ret = ERROR_NO_MORE_ITEMS;
407 else
408 ret = NetLocalGroupEnum (NULL, 0, (PBYTE *) &buf,
409 MAX_PREFERRED_LENGTH,
410 &max, &total, &resume);
411 if (ret == NERR_Success)
412 resume = ULONG_MAX;
413 else if (ret != ERROR_MORE_DATA)
415 cnt = max = resume = 0;
416 return NULL;
419 while (cnt < max)
421 cygsid sid;
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))
430 continue;
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 ())
435 continue;
436 fetch_user_arg_t arg;
437 arg.type = SID_arg;
438 arg.sid = &sid;
439 char *line = pg.fetch_account_from_windows (arg);
440 if (line)
441 return pg.add_account_post_fetch (line, false);
443 cnt = 0;
447 struct group *
448 gr_ent::getgrent (void)
450 if (state == rewound)
451 setent (true);
452 else
453 clear_cache ();
454 return (struct group *) getent ();
457 extern "C" void
458 setgrent ()
460 grent.setgrent ();
463 extern "C" struct group *
464 getgrent (void)
466 return grent.getgrent ();
469 extern "C" void
470 endgrent (void)
472 grent.endgrent ();
475 /* *_filtered functions are called from mkgroup */
476 void *
477 setgrent_filtered (int enums, PCWSTR enum_tdoms)
479 gr_ent *gr = new gr_ent;
480 if (gr)
481 gr->setgrent (enums, enum_tdoms);
482 return (void *) gr;
485 void *
486 getgrent_filtered (void *gr)
488 return (void *) ((gr_ent *) gr)->getgrent ();
491 void
492 endgrent_filtered (void *gr)
494 ((gr_ent *) gr)->endgrent ();
498 internal_getgroups (int gidsetsize, gid_t *grouplist, cyg_ldap *pldap)
500 NTSTATUS status;
501 HANDLE tok;
502 ULONG size;
503 PTOKEN_GROUPS groups;
504 PSID *sidp_buf;
505 ULONG scnt;
506 PLSA_REFERENCED_DOMAIN_LIST dlst = NULL;
507 PLSA_TRANSLATED_NAME nlst = NULL;
509 tmp_pathbuf tp;
510 struct group *grp;
511 int cnt = 0;
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],
517 pldap)))
519 if (cnt < gidsetsize)
520 grouplist[cnt] = grp->gr_gid;
521 ++cnt;
522 if (gidsetsize && cnt > gidsetsize)
524 cnt = -1;
525 break;
528 goto out;
531 /* If impersonated, use impersonation token. */
532 tok = cygheap->user.issetuid () ? cygheap->user.primary_token ()
533 : hProcToken;
535 /* Fetch groups from user token. */
536 groups = (PTOKEN_GROUPS) tp.w_get ();
537 status = NtQueryInformationToken (tok, TokenGroups, groups, 2 * NT_MAX_PATH,
538 &size);
539 if (!NT_SUCCESS (status))
541 debug_printf ("NtQueryInformationToken(TokenGroups) %y", status);
542 goto out;
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 ();
548 scnt = 0;
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)
555 continue;
556 if ((grp = internal_getgrsid_cachedonly (sid)))
558 if (cnt < gidsetsize)
559 grouplist[cnt] = grp->gr_gid;
560 ++cnt;
561 if (gidsetsize && cnt > gidsetsize)
563 cnt = -1;
564 goto out;
567 else
568 sidp_buf[scnt++] = sid;
570 /* If there are non-cached groups left, try to fetch them. */
571 if (scnt > 0)
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;
586 ++cnt;
587 if (gidsetsize && cnt > gidsetsize)
589 cnt = -1;
590 break;
594 goto out;
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);
601 if (!lsa)
603 debug_printf ("POLICY_LOOKUP_NAMES right not given?");
604 goto out;
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,
617 .dom = &empty,
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;
627 ++cnt;
628 if (gidsetsize && cnt > gidsetsize)
630 cnt = -1;
631 break;
638 out:
639 if (dlst)
640 LsaFreeMemory (dlst);
641 if (nlst)
642 LsaFreeMemory (nlst);
643 if (cnt == -1)
644 set_errno (EINVAL);
645 return cnt;
648 extern "C" int
649 getgroups (int gidsetsize, gid_t *grouplist)
651 cyg_ldap cldap;
653 return internal_getgroups (gidsetsize, grouplist, &cldap);
656 /* Core functionality of initgroups and getgrouplist. */
657 static void
658 get_groups (const char *user, gid_t gid, cygsidlist &gsids)
660 cyg_ldap cldap;
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))
669 gsids += grpsid;
670 cygheap->user.reimpersonate ();
673 extern "C" int
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);
685 return 0;
688 extern "C" int
689 getgrouplist (const char *user, gid_t gid, gid_t *groups, int *ngroups)
691 int ret = 0;
692 int cnt = 0;
693 struct group *grp;
694 cyg_ldap cldap;
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;
711 ++cnt;
713 if (cnt > *ngroups)
714 ret = -1;
715 else
716 ret = cnt;
717 *ngroups = cnt;
719 syscall_printf ( "%d = getgrouplist(%s, %u, %p, %d)",
720 ret, user, gid, groups, *ngroups);
721 return ret;
724 /* setgroups: standards? */
725 extern "C" int
726 setgroups (int ngroups, const gid_t *grouplist)
728 syscall_printf ("setgroups (%d)", ngroups);
729 if (ngroups < 0 || (ngroups > 0 && !grouplist))
731 set_errno (EINVAL);
732 return -1;
735 cygsidlist gsids (cygsidlist_alloc, ngroups);
736 struct group *grp;
737 cyg_ldap cldap;
739 if (ngroups && !gsids.sids)
740 return -1;
742 for (int gidx = 0; gidx < ngroups; ++gidx)
744 if ((grp = internal_getgrgid (grouplist[gidx], &cldap))
745 && gsids.addfromgr (grp))
746 continue;
747 debug_printf ("No sid found for gid %u", grouplist[gidx]);
748 gsids.free_sids ();
749 set_errno (EINVAL);
750 return -1;
752 cygheap->user.groups.update_supp (gsids);
753 return 0;