]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* grp.cc |
2 | ||
1fd5e000 CF |
3 | Original stubs by Jason Molenda of Cygnus Support, crash@cygnus.com |
4 | First implementation by Gunther Ebert, gunther.ebert@ixos-leipzig.de | |
5 | ||
6 | This file is part of Cygwin. | |
7 | ||
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. */ | |
11 | ||
4c8d72de | 12 | #include "winsup.h" |
a8cf6887 | 13 | #include <lm.h> |
bef55bb5 | 14 | #include <ntsecapi.h> |
0c1d8aaa | 15 | #include <assert.h> |
1fd5e000 CF |
16 | #include <stdio.h> |
17 | #include <stdlib.h> | |
169c465a | 18 | #include "cygerrno.h" |
e2ebe117 | 19 | #include "pinfo.h" |
f5e8e2be | 20 | #include "path.h" |
7ac61736 | 21 | #include "fhandler.h" |
47063f00 | 22 | #include "dtable.h" |
df63bd49 | 23 | #include "cygheap.h" |
12eac211 | 24 | #include "ntdll.h" |
a8cf6887 CV |
25 | #include "miscfuncs.h" |
26 | #include "ldap.h" | |
27 | #include "tls_pbuf.h" | |
1fd5e000 | 28 | |
61522196 | 29 | static char * NO_COPY_RO null_ptr; |
1fd5e000 | 30 | |
7905c4f1 | 31 | bool |
ac413374 | 32 | pwdgrp::parse_group () |
1fd5e000 | 33 | { |
1ca20a1c CV |
34 | pg_grp &grp = group ()[curr_lines]; |
35 | grp.g.gr_name = next_str (':'); | |
36 | if (!*grp.g.gr_name) | |
ac413374 | 37 | return false; |
1ca20a1c | 38 | grp.g.gr_passwd = next_str (':'); |
20de26eb CV |
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; | |
1ca20a1c | 43 | if (!next_num (grp.g.gr_gid)) |
65037056 | 44 | return false; |
20de26eb | 45 | /* Don't generate gr_mem entries. */ |
1ca20a1c | 46 | grp.g.gr_mem = &null_ptr; |
83b3f891 | 47 | cygsid csid; |
c7432b13 CV |
48 | if (csid.getfromgr_passwd (&grp.g)) |
49 | RtlCopySid (SECURITY_MAX_SID_SIZE, grp.sid, csid); | |
65037056 | 50 | return true; |
1fd5e000 CF |
51 | } |
52 | ||
1ca20a1c CV |
53 | muto NO_COPY pwdgrp::pglock; |
54 | ||
57394495 | 55 | void |
1ca20a1c | 56 | pwdgrp::init_grp () |
1fd5e000 | 57 | { |
1ca20a1c CV |
58 | pwdgrp_buf_elem_size = sizeof (pg_grp); |
59 | parse = &pwdgrp::parse_group; | |
60 | } | |
57394495 | 61 | |
1ca20a1c CV |
62 | struct group * |
63 | pwdgrp::find_group (cygpsid &sid) | |
64 | { | |
65 | for (ULONG i = 0; i < curr_lines; i++) | |
66 | if (sid == group ()[i].sid) | |
67 | return &group ()[i].g; | |
68 | return NULL; | |
69 | } | |
2f1086cb | 70 | |
1ca20a1c CV |
71 | struct group * |
72 | pwdgrp::find_group (const char *name) | |
f3c1c540 | 73 | { |
1ca20a1c CV |
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; | |
f3c1c540 TP |
78 | } |
79 | ||
1ca20a1c CV |
80 | struct group * |
81 | pwdgrp::find_group (gid_t gid) | |
f3c1c540 | 82 | { |
1ca20a1c CV |
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; | |
f3c1c540 TP |
87 | } |
88 | ||
61522196 | 89 | struct group * |
b39fa2c8 | 90 | internal_getgrsid (cygpsid &sid, cyg_ldap *pldap) |
647b92a7 | 91 | { |
1ca20a1c | 92 | struct group *ret; |
647b92a7 | 93 | |
1ca20a1c | 94 | cygheap->pg.nss_init (); |
29b7313d CV |
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; | |
1ca20a1c CV |
109 | if (cygheap->pg.nss_grp_files ()) |
110 | { | |
0e8dd884 | 111 | cygheap->pg.grp_cache.file.check_file (); |
1ca20a1c CV |
112 | if ((ret = cygheap->pg.grp_cache.file.add_group_from_file (sid))) |
113 | return ret; | |
114 | } | |
115 | if (cygheap->pg.nss_grp_db ()) | |
29b7313d | 116 | return cygheap->pg.grp_cache.win.add_group_from_windows (sid, pldap); |
647b92a7 CV |
117 | return NULL; |
118 | } | |
119 | ||
bef55bb5 CV |
120 | /* Like internal_getgrsid but return only already cached data, |
121 | NULL otherwise. */ | |
122 | static struct group * | |
123 | internal_getgrsid_cachedonly (cygpsid &sid) | |
124 | { | |
125 | struct group *ret; | |
126 | ||
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; | |
138 | } | |
139 | ||
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)) | |
ad8d295e | 147 | internal_getgrfull (fetch_acc_t &full_acc, cyg_ldap *pldap) |
bef55bb5 CV |
148 | { |
149 | struct group *ret; | |
150 | ||
151 | cygheap->pg.nss_init (); | |
4cb24051 CV |
152 | /* Skip local caches, internal_getgroups already called |
153 | internal_getgrsid_cachedonly. */ | |
bef55bb5 CV |
154 | if (cygheap->pg.nss_cygserver_caching () |
155 | && (ret = cygheap->pg.grp_cache.cygserver.add_group_from_cygserver | |
d2ef2331 | 156 | (full_acc.sid))) |
bef55bb5 CV |
157 | return ret; |
158 | if (cygheap->pg.nss_grp_files ()) | |
159 | { | |
160 | cygheap->pg.grp_cache.file.check_file (); | |
161 | if ((ret = cygheap->pg.grp_cache.file.add_group_from_file | |
d2ef2331 | 162 | (full_acc.sid))) |
bef55bb5 CV |
163 | return ret; |
164 | } | |
165 | if (cygheap->pg.nss_grp_db ()) | |
ad8d295e | 166 | return cygheap->pg.grp_cache.win.add_group_from_windows (full_acc, pldap); |
bef55bb5 CV |
167 | return NULL; |
168 | } | |
169 | ||
b211f4c1 CV |
170 | /* This function gets only called from mkgroup via cygwin_internal. */ |
171 | struct group * | |
172 | internal_getgrsid_from_db (cygpsid &sid) | |
173 | { | |
174 | cygheap->pg.nss_init (); | |
175 | return cygheap->pg.grp_cache.win.add_group_from_windows (sid); | |
176 | } | |
177 | ||
61522196 | 178 | struct group * |
b39fa2c8 | 179 | internal_getgrnam (const char *name, cyg_ldap *pldap) |
d6ffc075 | 180 | { |
1ca20a1c | 181 | struct group *ret; |
d6ffc075 | 182 | |
1ca20a1c | 183 | cygheap->pg.nss_init (); |
29b7313d CV |
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; | |
1ca20a1c CV |
198 | if (cygheap->pg.nss_grp_files ()) |
199 | { | |
0e8dd884 | 200 | cygheap->pg.grp_cache.file.check_file (); |
1ca20a1c CV |
201 | if ((ret = cygheap->pg.grp_cache.file.add_group_from_file (name))) |
202 | return ret; | |
203 | } | |
204 | if (cygheap->pg.nss_grp_db ()) | |
29b7313d | 205 | return cygheap->pg.grp_cache.win.add_group_from_windows (name, pldap); |
14ea5029 | 206 | return NULL; |
d6ffc075 CV |
207 | } |
208 | ||
61522196 | 209 | struct group * |
b39fa2c8 | 210 | internal_getgrgid (gid_t gid, cyg_ldap *pldap) |
d6ffc075 | 211 | { |
1ca20a1c | 212 | struct group *ret; |
d6ffc075 | 213 | |
1ca20a1c | 214 | cygheap->pg.nss_init (); |
29b7313d CV |
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; | |
1ca20a1c CV |
229 | if (cygheap->pg.nss_grp_files ()) |
230 | { | |
0e8dd884 | 231 | cygheap->pg.grp_cache.file.check_file (); |
1ca20a1c CV |
232 | if ((ret = cygheap->pg.grp_cache.file.add_group_from_file (gid))) |
233 | return ret; | |
234 | } | |
29b7313d | 235 | if (cygheap->pg.nss_grp_db () || gid == ILLEGAL_GID) |
b39fa2c8 | 236 | return cygheap->pg.grp_cache.win.add_group_from_windows (gid, pldap); |
d6ffc075 CV |
237 | return NULL; |
238 | } | |
239 | ||
68509b30 | 240 | extern "C" int |
61522196 CV |
241 | getgrgid_r (gid_t gid, struct group *grp, char *buffer, size_t bufsize, |
242 | struct group **result) | |
68509b30 CV |
243 | { |
244 | *result = NULL; | |
245 | ||
246 | if (!grp || !buffer) | |
247 | return ERANGE; | |
248 | ||
1ca20a1c | 249 | struct group *tempgr = internal_getgrgid (gid); |
e3778517 | 250 | pthread_testcancel (); |
68509b30 CV |
251 | if (!tempgr) |
252 | return 0; | |
253 | ||
20de26eb | 254 | /* Check needed buffer size. Deliberately ignore gr_mem. */ |
68509b30 CV |
255 | size_t needsize = strlen (tempgr->gr_name) + strlen (tempgr->gr_passwd) |
256 | + 2 + sizeof (char *); | |
68509b30 CV |
257 | if (needsize > bufsize) |
258 | return ERANGE; | |
259 | ||
20de26eb | 260 | /* Make a copy of tempgr. Deliberately ignore gr_mem. */ |
68509b30 CV |
261 | *result = grp; |
262 | grp->gr_gid = tempgr->gr_gid; | |
b89fb103 CV |
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); | |
20de26eb | 266 | grp->gr_mem[0] = NULL; |
68509b30 CV |
267 | return 0; |
268 | } | |
269 | ||
20de26eb CV |
270 | /* getgrgid/getgrnam are not reentrant. */ |
271 | static struct { | |
272 | struct group g; | |
273 | char *buf; | |
274 | size_t bufsiz; | |
275 | } app_gr; | |
276 | ||
277 | static struct group * | |
278 | getgr_cp (struct group *tempgr) | |
279 | { | |
280 | if (!tempgr) | |
281 | return NULL; | |
282 | pg_grp *gr = (pg_grp *) tempgr; | |
283 | if (app_gr.bufsiz < gr->len) | |
284 | { | |
285 | char *newbuf = (char *) realloc (app_gr.buf, gr->len); | |
286 | if (!newbuf) | |
287 | { | |
288 | set_errno (ENOMEM); | |
289 | return NULL; | |
290 | } | |
291 | app_gr.buf = newbuf; | |
292 | app_gr.bufsiz = gr->len; | |
293 | } | |
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; | |
300 | } | |
301 | ||
61522196 | 302 | extern "C" struct group * |
2d9b4876 | 303 | getgrgid (gid_t gid) |
57196405 | 304 | { |
20de26eb CV |
305 | struct group *tempgr = internal_getgrgid (gid); |
306 | pthread_testcancel (); | |
307 | return getgr_cp (tempgr); | |
1fd5e000 CF |
308 | } |
309 | ||
68509b30 | 310 | extern "C" int |
61522196 CV |
311 | getgrnam_r (const char *nam, struct group *grp, char *buffer, |
312 | size_t bufsize, struct group **result) | |
68509b30 CV |
313 | { |
314 | *result = NULL; | |
315 | ||
316 | if (!grp || !buffer) | |
317 | return ERANGE; | |
318 | ||
1ca20a1c | 319 | struct group *tempgr = internal_getgrnam (nam); |
e3778517 | 320 | pthread_testcancel (); |
68509b30 CV |
321 | if (!tempgr) |
322 | return 0; | |
323 | ||
20de26eb | 324 | /* Check needed buffer size. Deliberately ignore gr_mem. */ |
68509b30 CV |
325 | size_t needsize = strlen (tempgr->gr_name) + strlen (tempgr->gr_passwd) |
326 | + 2 + sizeof (char *); | |
68509b30 CV |
327 | if (needsize > bufsize) |
328 | return ERANGE; | |
329 | ||
20de26eb | 330 | /* Make a copy of tempgr. Deliberately ignore gr_mem. */ |
68509b30 CV |
331 | *result = grp; |
332 | grp->gr_gid = tempgr->gr_gid; | |
b89fb103 CV |
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); | |
20de26eb | 336 | grp->gr_mem[0] = NULL; |
68509b30 CV |
337 | return 0; |
338 | } | |
339 | ||
61522196 | 340 | extern "C" struct group * |
2d9b4876 | 341 | getgrnam (const char *name) |
1fd5e000 | 342 | { |
20de26eb CV |
343 | struct group *tempgr = internal_getgrnam (name); |
344 | pthread_testcancel (); | |
345 | return getgr_cp (tempgr); | |
1fd5e000 CF |
346 | } |
347 | ||
a8cf6887 CV |
348 | /* getgrent functions are not reentrant. */ |
349 | static gr_ent grent; | |
1fd5e000 | 350 | |
a8cf6887 CV |
351 | void * |
352 | gr_ent::enumerate_caches () | |
1fd5e000 | 353 | { |
29b7313d | 354 | switch (max) |
1ca20a1c | 355 | { |
29b7313d CV |
356 | case 0: |
357 | if (cygheap->pg.nss_cygserver_caching ()) | |
358 | { | |
359 | pwdgrp &grc = cygheap->pg.grp_cache.cygserver; | |
360 | if (cnt < grc.cached_groups ()) | |
361 | return &grc.group ()[cnt++].g; | |
362 | } | |
a8cf6887 CV |
363 | cnt = 0; |
364 | max = 1; | |
50ad1980 | 365 | fallthrough; |
29b7313d CV |
366 | case 1: |
367 | if (from_files) | |
368 | { | |
369 | pwdgrp &grf = cygheap->pg.grp_cache.file; | |
370 | grf.check_file (); | |
371 | if (cnt < grf.cached_groups ()) | |
372 | return &grf.group ()[cnt++].g; | |
373 | } | |
374 | cnt = 0; | |
a5a75a5a | 375 | max = 2; |
50ad1980 | 376 | fallthrough; |
29b7313d CV |
377 | case 2: |
378 | if (from_db) | |
379 | { | |
380 | pwdgrp &grw = cygheap->pg.grp_cache.win; | |
381 | if (cnt < grw.cached_groups ()) | |
382 | return &grw.group ()[cnt++].g; | |
383 | } | |
384 | break; | |
1ca20a1c | 385 | } |
a8cf6887 | 386 | cnt = max = 0; |
1fd5e000 CF |
387 | return NULL; |
388 | } | |
389 | ||
a8cf6887 CV |
390 | void * |
391 | gr_ent::enumerate_local () | |
392 | { | |
393 | while (true) | |
394 | { | |
395 | if (!cnt) | |
396 | { | |
397 | DWORD total; | |
398 | NET_API_STATUS ret; | |
399 | ||
400 | if (buf) | |
401 | { | |
402 | NetApiBufferFree (buf); | |
403 | buf = NULL; | |
404 | } | |
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) | |
414 | { | |
415 | cnt = max = resume = 0; | |
416 | return NULL; | |
417 | } | |
418 | } | |
419 | while (cnt < max) | |
420 | { | |
421 | cygsid sid; | |
fc3a3524 | 422 | DWORD slen = SECURITY_MAX_SID_SIZE; |
a8cf6887 CV |
423 | WCHAR dom[DNLEN + 1]; |
424 | DWORD dlen = DNLEN + 1; | |
425 | SID_NAME_USE acc_type; | |
426 | ||
15e82eef CV |
427 | if (!LookupAccountNameW (NULL, |
428 | ((PLOCALGROUP_INFO_0) buf)[cnt++].lgrpi0_name, | |
429 | sid, &slen, dom, &dlen, &acc_type)) | |
430 | continue; | |
5f586adc CV |
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 */ | |
15e82eef | 437 | && sid_sub_auth (sid, 0) == SECURITY_BUILTIN_DOMAIN_RID |
5f586adc CV |
438 | && sid_sub_auth (sid, 1) != DOMAIN_ALIAS_RID_POWER_USERS |
439 | && sid_sub_auth (sid, 1) != DOMAIN_ALIAS_RID_DEVICE_OWNERS) | |
15e82eef | 440 | continue; |
a8cf6887 CV |
441 | fetch_user_arg_t arg; |
442 | arg.type = SID_arg; | |
443 | arg.sid = &sid; | |
0e8dd884 | 444 | char *line = pg.fetch_account_from_windows (arg); |
a8cf6887 CV |
445 | if (line) |
446 | return pg.add_account_post_fetch (line, false); | |
447 | } | |
448 | cnt = 0; | |
449 | } | |
450 | } | |
451 | ||
452 | struct group * | |
453 | gr_ent::getgrent (void) | |
454 | { | |
455 | if (state == rewound) | |
456 | setent (true); | |
457 | else | |
458 | clear_cache (); | |
459 | return (struct group *) getent (); | |
460 | } | |
461 | ||
462 | extern "C" void | |
463 | setgrent () | |
464 | { | |
465 | grent.setgrent (); | |
466 | } | |
467 | ||
468 | extern "C" struct group * | |
2d9b4876 | 469 | getgrent (void) |
a8cf6887 CV |
470 | { |
471 | return grent.getgrent (); | |
472 | } | |
473 | ||
38c77307 | 474 | extern "C" void |
a8cf6887 | 475 | endgrent (void) |
1fd5e000 | 476 | { |
a8cf6887 | 477 | grent.endgrent (); |
1fd5e000 CF |
478 | } |
479 | ||
036f56cf CV |
480 | /* *_filtered functions are called from mkgroup */ |
481 | void * | |
482 | setgrent_filtered (int enums, PCWSTR enum_tdoms) | |
483 | { | |
484 | gr_ent *gr = new gr_ent; | |
485 | if (gr) | |
486 | gr->setgrent (enums, enum_tdoms); | |
487 | return (void *) gr; | |
488 | } | |
489 | ||
490 | void * | |
491 | getgrent_filtered (void *gr) | |
492 | { | |
493 | return (void *) ((gr_ent *) gr)->getgrent (); | |
494 | } | |
495 | ||
496 | void | |
497 | endgrent_filtered (void *gr) | |
498 | { | |
499 | ((gr_ent *) gr)->endgrent (); | |
500 | } | |
501 | ||
6cc7c925 | 502 | int |
6f93f1d6 | 503 | internal_getgroups (int gidsetsize, gid_t *grouplist, cyg_ldap *pldap) |
1fd5e000 | 504 | { |
12eac211 | 505 | NTSTATUS status; |
b39fa2c8 | 506 | HANDLE tok; |
12eac211 | 507 | ULONG size; |
bef55bb5 CV |
508 | PTOKEN_GROUPS groups; |
509 | PSID *sidp_buf; | |
510 | ULONG scnt; | |
511 | PLSA_REFERENCED_DOMAIN_LIST dlst = NULL; | |
512 | PLSA_TRANSLATED_NAME nlst = NULL; | |
513 | ||
514 | tmp_pathbuf tp; | |
1ca20a1c | 515 | struct group *grp; |
bef55bb5 | 516 | int cnt = 0; |
1fd5e000 | 517 | |
b39fa2c8 | 518 | if (cygheap->user.groups.issetgroups ()) |
4ce377c9 | 519 | { |
1ca20a1c | 520 | for (int pg = 0; pg < cygheap->user.groups.sgsids.count (); ++pg) |
b39fa2c8 | 521 | if ((grp = internal_getgrsid (cygheap->user.groups.sgsids.sids[pg], |
6cc7c925 | 522 | pldap))) |
1ca20a1c CV |
523 | { |
524 | if (cnt < gidsetsize) | |
525 | grouplist[cnt] = grp->gr_gid; | |
526 | ++cnt; | |
527 | if (gidsetsize && cnt > gidsetsize) | |
bef55bb5 CV |
528 | { |
529 | cnt = -1; | |
530 | break; | |
531 | } | |
1ca20a1c | 532 | } |
bef55bb5 | 533 | goto out; |
4ce377c9 | 534 | } |
68a3f0d3 | 535 | |
68a3f0d3 | 536 | /* If impersonated, use impersonation token. */ |
bef55bb5 CV |
537 | tok = cygheap->user.issetuid () ? cygheap->user.primary_token () |
538 | : hProcToken; | |
539 | ||
540 | /* Fetch groups from user token. */ | |
541 | groups = (PTOKEN_GROUPS) tp.w_get (); | |
542 | status = NtQueryInformationToken (tok, TokenGroups, groups, 2 * NT_MAX_PATH, | |
543 | &size); | |
544 | if (!NT_SUCCESS (status)) | |
c4548fb4 | 545 | { |
4cb24051 | 546 | debug_printf ("NtQueryInformationToken(TokenGroups) %y", status); |
bef55bb5 CV |
547 | goto out; |
548 | } | |
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 (); | |
553 | scnt = 0; | |
554 | for (DWORD pg = 0; pg < groups->GroupCount; ++pg) | |
555 | { | |
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) | |
560 | continue; | |
561 | if ((grp = internal_getgrsid_cachedonly (sid))) | |
562 | { | |
563 | if (cnt < gidsetsize) | |
564 | grouplist[cnt] = grp->gr_gid; | |
565 | ++cnt; | |
566 | if (gidsetsize && cnt > gidsetsize) | |
567 | { | |
568 | cnt = -1; | |
569 | goto out; | |
570 | } | |
571 | } | |
b74bc883 | 572 | else |
bef55bb5 CV |
573 | sidp_buf[scnt++] = sid; |
574 | } | |
4cb24051 | 575 | /* If there are non-cached groups left, try to fetch them. */ |
bef55bb5 CV |
576 | if (scnt > 0) |
577 | { | |
4cb24051 CV |
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 ()) | |
583 | { | |
584 | for (DWORD pg = 0; pg < scnt; ++pg) | |
585 | { | |
586 | cygpsid sid = sidp_buf[pg]; | |
587 | if ((grp = internal_getgrsid (sid, NULL))) | |
588 | { | |
589 | if (cnt < gidsetsize) | |
590 | grouplist[cnt] = grp->gr_gid; | |
591 | ++cnt; | |
592 | if (gidsetsize && cnt > gidsetsize) | |
593 | { | |
594 | cnt = -1; | |
595 | break; | |
596 | } | |
597 | } | |
598 | } | |
599 | goto out; | |
600 | } | |
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. */ | |
bef55bb5 CV |
604 | status = STATUS_ACCESS_DENIED; |
605 | HANDLE lsa = lsa_open_policy (NULL, POLICY_LOOKUP_NAMES); | |
606 | if (!lsa) | |
607 | { | |
4cb24051 | 608 | debug_printf ("POLICY_LOOKUP_NAMES right not given?"); |
bef55bb5 CV |
609 | goto out; |
610 | } | |
611 | status = LsaLookupSids (lsa, scnt, sidp_buf, &dlst, &nlst); | |
612 | lsa_close_policy (lsa); | |
12eac211 | 613 | if (NT_SUCCESS (status)) |
1ff9f4b9 | 614 | { |
bef55bb5 | 615 | for (ULONG ncnt = 0; ncnt < scnt; ++ncnt) |
1ca20a1c | 616 | { |
71a897e4 | 617 | static UNICODE_STRING empty = { 0, 0, (PWSTR) L"" }; |
ad8d295e | 618 | fetch_acc_t full_acc = |
bef55bb5 CV |
619 | { |
620 | .sid = sidp_buf[ncnt], | |
621 | .name = &nlst[ncnt].Name, | |
71a897e4 | 622 | .dom = &empty, |
bef55bb5 CV |
623 | .acc_type = nlst[ncnt].Use |
624 | }; | |
71a897e4 CV |
625 | |
626 | if (nlst[ncnt].DomainIndex >= 0) | |
627 | full_acc.dom = &dlst->Domains[nlst[ncnt].DomainIndex].Name; | |
ad8d295e | 628 | if ((grp = internal_getgrfull (full_acc, pldap))) |
1ca20a1c | 629 | { |
9b54770b CV |
630 | if (cnt < gidsetsize) |
631 | grouplist[cnt] = grp->gr_gid; | |
632 | ++cnt; | |
633 | if (gidsetsize && cnt > gidsetsize) | |
bef55bb5 CV |
634 | { |
635 | cnt = -1; | |
4cb24051 | 636 | break; |
bef55bb5 | 637 | } |
1ca20a1c CV |
638 | } |
639 | } | |
1ff9f4b9 | 640 | } |
c4548fb4 | 641 | } |
4f2aac14 | 642 | |
bef55bb5 CV |
643 | out: |
644 | if (dlst) | |
645 | LsaFreeMemory (dlst); | |
646 | if (nlst) | |
647 | LsaFreeMemory (nlst); | |
648 | if (cnt == -1) | |
649 | set_errno (EINVAL); | |
650 | return cnt; | |
1fd5e000 CF |
651 | } |
652 | ||
6cc7c925 | 653 | extern "C" int |
2d9b4876 | 654 | getgroups (int gidsetsize, gid_t *grouplist) |
6cc7c925 CV |
655 | { |
656 | cyg_ldap cldap; | |
657 | ||
658 | return internal_getgroups (gidsetsize, grouplist, &cldap); | |
659 | } | |
660 | ||
0c1d8aaa | 661 | /* Core functionality of initgroups and getgrouplist. */ |
ccd40e46 | 662 | static void |
0c1d8aaa | 663 | get_groups (const char *user, gid_t gid, cygsidlist &gsids) |
57196405 | 664 | { |
b39fa2c8 CV |
665 | cyg_ldap cldap; |
666 | ||
a76877e9 | 667 | cygheap->user.deimpersonate (); |
b39fa2c8 CV |
668 | struct passwd *pw = internal_getpwnam (user, &cldap); |
669 | struct group *grp = internal_getgrgid (gid, &cldap); | |
a76877e9 | 670 | cygsid usersid, grpsid; |
ee6ed1db | 671 | if (usersid.getfrompw (pw)) |
02373d8b | 672 | get_server_groups (gsids, usersid, NO_CHK_DISABLED); |
7f57a4ea | 673 | if (gid != ILLEGAL_GID && grpsid.getfromgr (grp)) |
ee6ed1db | 674 | gsids += grpsid; |
a76877e9 | 675 | cygheap->user.reimpersonate (); |
1fd5e000 CF |
676 | } |
677 | ||
38c77307 | 678 | extern "C" int |
2d9b4876 | 679 | initgroups (const char *user, gid_t gid) |
0c1d8aaa | 680 | { |
0c1d8aaa CV |
681 | assert (user != NULL); |
682 | cygsidlist tmp_gsids (cygsidlist_auto, 12); | |
ccd40e46 CV |
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); | |
690 | return 0; | |
0c1d8aaa CV |
691 | } |
692 | ||
0c1d8aaa CV |
693 | extern "C" int |
694 | getgrouplist (const char *user, gid_t gid, gid_t *groups, int *ngroups) | |
695 | { | |
ccd40e46 CV |
696 | int ret = 0; |
697 | int cnt = 0; | |
1ca20a1c | 698 | struct group *grp; |
b39fa2c8 | 699 | cyg_ldap cldap; |
0c1d8aaa CV |
700 | |
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); | |
708 | ||
709 | cygsidlist tmp_gsids (cygsidlist_auto, 12); | |
ccd40e46 CV |
710 | get_groups (user, gid, tmp_gsids); |
711 | for (int i = 0; i < tmp_gsids.count (); i++) | |
b39fa2c8 | 712 | if ((grp = internal_getgrsid (tmp_gsids.sids[i], &cldap)) != NULL) |
ccd40e46 CV |
713 | { |
714 | if (groups && cnt < *ngroups) | |
1ca20a1c | 715 | groups[cnt] = grp->gr_gid; |
ccd40e46 CV |
716 | ++cnt; |
717 | } | |
718 | if (cnt > *ngroups) | |
719 | ret = -1; | |
1e3a1509 CV |
720 | else |
721 | ret = cnt; | |
6681d11f | 722 | *ngroups = cnt; |
ccd40e46 | 723 | |
b9aa8149 | 724 | syscall_printf ( "%d = getgrouplist(%s, %u, %p, %d)", |
0c1d8aaa CV |
725 | ret, user, gid, groups, *ngroups); |
726 | return ret; | |
1fd5e000 | 727 | } |
5519d543 | 728 | |
2d9b4876 | 729 | /* setgroups: standards? */ |
38c77307 | 730 | extern "C" int |
2d9b4876 | 731 | setgroups (int ngroups, const gid_t *grouplist) |
5519d543 | 732 | { |
2d9b4876 | 733 | syscall_printf ("setgroups (%d)", ngroups); |
5519d543 CV |
734 | if (ngroups < 0 || (ngroups > 0 && !grouplist)) |
735 | { | |
736 | set_errno (EINVAL); | |
737 | return -1; | |
738 | } | |
739 | ||
5519d543 | 740 | cygsidlist gsids (cygsidlist_alloc, ngroups); |
1ca20a1c | 741 | struct group *grp; |
b39fa2c8 | 742 | cyg_ldap cldap; |
5519d543 CV |
743 | |
744 | if (ngroups && !gsids.sids) | |
745 | return -1; | |
746 | ||
747 | for (int gidx = 0; gidx < ngroups; ++gidx) | |
748 | { | |
b39fa2c8 | 749 | if ((grp = internal_getgrgid (grouplist[gidx], &cldap)) |
1ca20a1c | 750 | && gsids.addfromgr (grp)) |
b62b8d7c | 751 | continue; |
61522196 | 752 | debug_printf ("No sid found for gid %u", grouplist[gidx]); |
5519d543 CV |
753 | gsids.free_sids (); |
754 | set_errno (EINVAL); | |
755 | return -1; | |
5519d543 CV |
756 | } |
757 | cygheap->user.groups.update_supp (gsids); | |
758 | return 0; | |
759 | } |