]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* mkgroup.c: |
2 | ||
df0f949c | 3 | Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, |
61522196 | 4 | 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. |
1fd5e000 CF |
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 | ||
a1e19903 | 12 | #define _WIN32_WINNT 0x0600 |
92b499ac | 13 | #include <errno.h> |
1fd5e000 CF |
14 | #include <ctype.h> |
15 | #include <stdlib.h> | |
16 | #include <wchar.h> | |
0bdab5c8 | 17 | #include <wctype.h> |
73535010 | 18 | #include <locale.h> |
1fd5e000 | 19 | #include <stdio.h> |
a1e19903 | 20 | #include <unistd.h> |
61522196 | 21 | #include <inttypes.h> |
315f8fd3 | 22 | #include <getopt.h> |
a1e19903 CV |
23 | #include <io.h> |
24 | #include <sys/fcntl.h> | |
25 | #include <sys/cygwin.h> | |
92b499ac | 26 | #include <cygwin/version.h> |
a1e19903 CV |
27 | #include <windows.h> |
28 | #include <lm.h> | |
375a780e CV |
29 | #include <wininet.h> |
30 | #include <iptypes.h> | |
eeec2a48 | 31 | #include <ntsecapi.h> |
a1e19903 | 32 | #include <dsgetdc.h> |
eeec2a48 | 33 | #include <ntdef.h> |
71d8f118 | 34 | #include "loadlib.h" |
1fd5e000 | 35 | |
d829da14 CV |
36 | #define print_win_error(x) _print_win_error(x, __LINE__) |
37 | ||
375a780e CV |
38 | #define MAX_SID_LEN 40 |
39 | ||
1fd5e000 CF |
40 | SID_IDENTIFIER_AUTHORITY sid_world_auth = {SECURITY_WORLD_SID_AUTHORITY}; |
41 | SID_IDENTIFIER_AUTHORITY sid_nt_auth = {SECURITY_NT_AUTHORITY}; | |
42 | ||
43 | #ifndef min | |
44 | #define min(a,b) (((a)<(b))?(a):(b)) | |
45 | #endif | |
46 | ||
a1e19903 CV |
47 | typedef struct |
48 | { | |
49 | char *str; | |
f9519bcd CV |
50 | DWORD id_offset; |
51 | BOOL domain; | |
a1e19903 CV |
52 | BOOL with_dom; |
53 | } domlist_t; | |
54 | ||
6510edf4 | 55 | static void |
a1e19903 CV |
56 | _print_win_error (DWORD code, int line) |
57 | { | |
58 | char buf[4096]; | |
59 | ||
60 | if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | |
61 | | FORMAT_MESSAGE_IGNORE_INSERTS, | |
62 | NULL, | |
63 | code, | |
64 | MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), | |
65 | (LPTSTR) buf, sizeof (buf), NULL)) | |
61522196 CV |
66 | fprintf (stderr, "mkgroup (%d): [%" PRIu32 "] %s", |
67 | line, (unsigned int) code, buf); | |
a1e19903 | 68 | else |
61522196 CV |
69 | fprintf (stderr, "mkgroup (%d): error %" PRIu32 , |
70 | line, (unsigned int) code); | |
a1e19903 CV |
71 | } |
72 | ||
a1e19903 CV |
73 | static PWCHAR |
74 | get_dcname (char *domain) | |
75 | { | |
76 | static WCHAR server[INTERNET_MAX_HOST_NAME_LENGTH + 1]; | |
77 | DWORD rc; | |
a1e19903 CV |
78 | WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1]; |
79 | PDOMAIN_CONTROLLER_INFOW pdci = NULL; | |
80 | ||
13a20f95 | 81 | if (domain) |
a1e19903 | 82 | { |
13a20f95 CV |
83 | mbstowcs (domain_name, domain, strlen (domain) + 1); |
84 | rc = DsGetDcNameW (NULL, domain_name, NULL, NULL, 0, &pdci); | |
a1e19903 CV |
85 | } |
86 | else | |
13a20f95 CV |
87 | rc = DsGetDcNameW (NULL, NULL, NULL, NULL, 0, &pdci); |
88 | if (rc != ERROR_SUCCESS) | |
a1e19903 | 89 | { |
13a20f95 CV |
90 | print_win_error (rc); |
91 | return (PWCHAR) -1; | |
a1e19903 | 92 | } |
13a20f95 CV |
93 | wcscpy (server, pdci->DomainControllerName); |
94 | NetApiBufferFree (pdci); | |
a1e19903 CV |
95 | return server; |
96 | } | |
97 | ||
6510edf4 | 98 | static char * |
0bdab5c8 | 99 | put_sid (PSID psid) |
1fd5e000 CF |
100 | { |
101 | static char s[512]; | |
102 | char t[32]; | |
103 | DWORD i; | |
104 | ||
105 | strcpy (s, "S-1-"); | |
0bdab5c8 | 106 | sprintf(t, "%u", GetSidIdentifierAuthority (psid)->Value[5]); |
1fd5e000 | 107 | strcat (s, t); |
0bdab5c8 | 108 | for (i = 0; i < *GetSidSubAuthorityCount (psid); ++i) |
1fd5e000 | 109 | { |
61522196 | 110 | sprintf(t, "-%" PRIu32 , (unsigned int) *GetSidSubAuthority (psid, i)); |
1fd5e000 CF |
111 | strcat (s, t); |
112 | } | |
113 | return s; | |
114 | } | |
115 | ||
375a780e CV |
116 | typedef struct { |
117 | BYTE Revision; | |
118 | BYTE SubAuthorityCount; | |
119 | SID_IDENTIFIER_AUTHORITY IdentifierAuthority; | |
120 | DWORD SubAuthority[8]; | |
121 | } DBGSID, *PDBGSID; | |
122 | ||
123 | #define MAX_BUILTIN_SIDS 100 /* Should be enough for the forseable future. */ | |
124 | DBGSID builtin_sid_list[MAX_BUILTIN_SIDS]; | |
125 | DWORD builtin_sid_cnt; | |
126 | ||
9258eca9 CV |
127 | typedef struct { |
128 | PSID psid; | |
129 | int buffer[10]; | |
130 | } sidbuf; | |
131 | ||
6510edf4 CF |
132 | static sidbuf curr_pgrp; |
133 | static BOOL got_curr_pgrp = FALSE; | |
9258eca9 | 134 | |
6510edf4 | 135 | static void |
9258eca9 CV |
136 | fetch_current_pgrp_sid () |
137 | { | |
138 | DWORD len; | |
139 | HANDLE ptok; | |
140 | ||
141 | if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &ptok) | |
142 | || !GetTokenInformation (ptok, TokenPrimaryGroup, &curr_pgrp, | |
143 | sizeof curr_pgrp, &len) | |
144 | || !CloseHandle (ptok)) | |
145 | { | |
146 | print_win_error (GetLastError ()); | |
147 | return; | |
148 | } | |
149 | } | |
150 | ||
6510edf4 | 151 | static void |
9258eca9 CV |
152 | current_group (const char *sep, DWORD id_offset) |
153 | { | |
154 | WCHAR grp[GNLEN + 1]; | |
155 | WCHAR dom[MAX_DOMAIN_NAME_LEN + 1]; | |
156 | DWORD glen = GNLEN + 1; | |
157 | DWORD dlen = MAX_DOMAIN_NAME_LEN + 1; | |
158 | int gid; | |
159 | SID_NAME_USE acc_type; | |
160 | ||
161 | if (!curr_pgrp.psid | |
162 | || !LookupAccountSidW (NULL, curr_pgrp.psid, grp, &glen, dom, &dlen, | |
163 | &acc_type)) | |
164 | { | |
165 | print_win_error (GetLastError ()); | |
166 | return; | |
167 | } | |
168 | gid = *GetSidSubAuthority (curr_pgrp.psid, | |
169 | *GetSidSubAuthorityCount(curr_pgrp.psid) - 1); | |
61522196 | 170 | printf ("%ls%s%ls:%s:%" PRIu32 ":\n", |
9258eca9 CV |
171 | sep ? dom : L"", |
172 | sep ?: "", | |
173 | grp, | |
174 | put_sid (curr_pgrp.psid), | |
61522196 | 175 | (unsigned int) (id_offset + gid)); |
9258eca9 CV |
176 | } |
177 | ||
6510edf4 | 178 | static void |
f9519bcd | 179 | enum_unix_groups (domlist_t *dom_or_machine, const char *sep, DWORD id_offset, |
0bdab5c8 CV |
180 | char *unix_grp_list) |
181 | { | |
182 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; | |
183 | PWCHAR servername = NULL; | |
184 | char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL; | |
185 | BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE; | |
186 | SID_IDENTIFIER_AUTHORITY auth = { { 0, 0, 0, 0, 0, 22 } }; | |
187 | char *gstr, *grp_list; | |
188 | WCHAR grp[GNLEN + sizeof ("Unix Group\\") + 1]; | |
189 | WCHAR dom[MAX_DOMAIN_NAME_LEN + 1]; | |
190 | DWORD glen, dlen, sidlen; | |
191 | PSID psid; | |
192 | char psid_buffer[MAX_SID_LEN]; | |
193 | SID_NAME_USE acc_type; | |
194 | ||
195 | if (!d_or_m) | |
196 | return; | |
197 | ||
198 | int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1); | |
199 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) | |
200 | { | |
201 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", | |
92b499ac | 202 | program_invocation_short_name, d_or_m); |
0bdab5c8 CV |
203 | return; |
204 | } | |
205 | servername = machine; | |
206 | ||
207 | if (!AllocateAndInitializeSid (&auth, 2, 2, 0, 0, 0, 0, 0, 0, 0, &psid)) | |
208 | return; | |
209 | ||
210 | if (!(grp_list = strdup (unix_grp_list))) | |
211 | { | |
212 | FreeSid (psid); | |
213 | return; | |
214 | } | |
215 | ||
216 | for (gstr = strtok (grp_list, ","); gstr; gstr = strtok (NULL, ",")) | |
217 | { | |
05e6f7b2 | 218 | if (!isdigit ((unsigned char) gstr[0]) && gstr[0] != '-') |
0bdab5c8 CV |
219 | { |
220 | PWCHAR p = wcpcpy (grp, L"Unix Group\\"); | |
221 | ret = mbstowcs (p, gstr, GNLEN + 1); | |
222 | if (ret < 1 || ret >= GNLEN + 1) | |
223 | fprintf (stderr, "%s: Invalid group name '%s'. Skipping...\n", | |
92b499ac | 224 | program_invocation_short_name, gstr); |
0bdab5c8 CV |
225 | else if (LookupAccountNameW (servername, grp, |
226 | psid = (PSID) psid_buffer, | |
227 | (sidlen = MAX_SID_LEN, &sidlen), | |
228 | dom, | |
229 | (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen), | |
230 | &acc_type)) | |
61522196 | 231 | printf ("%s%s%ls:%s:%" PRIu32 ":\n", |
0bdab5c8 CV |
232 | with_dom ? "Unix Group" : "", |
233 | with_dom ? sep : "", | |
234 | p, | |
235 | put_sid (psid), | |
61522196 | 236 | (unsigned int) (id_offset + |
0bdab5c8 | 237 | *GetSidSubAuthority (psid, |
61522196 | 238 | *GetSidSubAuthorityCount(psid) - 1))); |
0bdab5c8 CV |
239 | } |
240 | else | |
241 | { | |
242 | DWORD start, stop; | |
243 | char *p = gstr; | |
244 | if (*p == '-') | |
245 | start = 0; | |
246 | else | |
247 | start = strtol (p, &p, 10); | |
248 | if (!*p) | |
249 | stop = start; | |
05e6f7b2 | 250 | else if (*p++ != '-' || !isdigit ((unsigned char) *p) |
0bdab5c8 CV |
251 | || (stop = strtol (p, &p, 10)) < start || *p) |
252 | { | |
253 | fprintf (stderr, "%s: Malformed unix group list entry '%s'. " | |
92b499ac CV |
254 | "Skipping...\n", |
255 | program_invocation_short_name, gstr); | |
0bdab5c8 CV |
256 | continue; |
257 | } | |
258 | for (; start <= stop; ++ start) | |
259 | { | |
260 | *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1) | |
261 | = start; | |
262 | if (LookupAccountSidW (servername, psid, | |
263 | grp, (glen = GNLEN + 1, &glen), | |
264 | dom, | |
265 | (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen), | |
266 | &acc_type) | |
267 | && !iswdigit (grp[0])) | |
61522196 | 268 | printf ("%s%s%ls:%s:%" PRIu32 ":\n", |
0bdab5c8 CV |
269 | with_dom ? "Unix Group" : "", |
270 | with_dom ? sep : "", | |
271 | grp, | |
272 | put_sid (psid), | |
61522196 | 273 | (unsigned int) (id_offset + start)); |
0bdab5c8 CV |
274 | } |
275 | } | |
276 | } | |
277 | ||
278 | free (grp_list); | |
279 | FreeSid (psid); | |
280 | } | |
281 | ||
6510edf4 | 282 | static int |
a1e19903 | 283 | enum_local_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep, |
6510edf4 CF |
284 | DWORD id_offset, char *disp_groupname, int print_builtin, |
285 | int print_current) | |
1fd5e000 | 286 | { |
a1e19903 CV |
287 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; |
288 | PWCHAR servername = NULL; | |
289 | char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL; | |
290 | BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE; | |
1fd5e000 CF |
291 | LOCALGROUP_INFO_0 *buffer; |
292 | DWORD entriesread = 0; | |
293 | DWORD totalentries = 0; | |
61522196 | 294 | DWORD_PTR resume_handle = 0; |
a1e19903 | 295 | WCHAR gname[GNLEN + 1]; |
0ac91154 | 296 | DWORD rc; |
1fd5e000 | 297 | |
a1e19903 CV |
298 | if (domain) |
299 | { | |
300 | servername = get_dcname (d_or_m); | |
301 | if (servername == (PWCHAR) -1) | |
302 | return 1; | |
303 | } | |
304 | else if (d_or_m) | |
305 | { | |
306 | int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1); | |
307 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) | |
6510edf4 | 308 | { |
a1e19903 | 309 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", |
92b499ac | 310 | program_invocation_short_name, d_or_m); |
a1e19903 CV |
311 | return 1; |
312 | } | |
313 | servername = machine; | |
314 | } | |
315 | ||
1fd5e000 CF |
316 | do |
317 | { | |
318 | DWORD i; | |
1fd5e000 | 319 | |
91dd009e | 320 | if (disp_groupname) |
e3118d88 | 321 | { |
a1e19903 | 322 | mbstowcs (gname, disp_groupname, GNLEN + 1); |
bba48953 CV |
323 | rc = NetLocalGroupGetInfo (servername, gname, 0, (void *) &buffer); |
324 | if (rc == ERROR_SUCCESS) | |
325 | entriesread = 1; | |
91dd009e CV |
326 | /* Allow further searching for the group and avoid annoying |
327 | error messages just because the group is not a local group or | |
328 | the group hasn't been found. */ | |
329 | else if (rc == ERROR_NO_SUCH_ALIAS || rc == NERR_GroupNotFound) | |
330 | return 0; | |
e3118d88 | 331 | } |
6510edf4 | 332 | else |
375a780e CV |
333 | rc = NetLocalGroupEnum (servername, 0, (void *) &buffer, |
334 | MAX_PREFERRED_LENGTH, &entriesread, | |
335 | &totalentries, &resume_handle); | |
1fd5e000 CF |
336 | switch (rc) |
337 | { | |
338 | case ERROR_ACCESS_DENIED: | |
a1e19903 CV |
339 | print_win_error (rc); |
340 | return 1; | |
1fd5e000 CF |
341 | |
342 | case ERROR_MORE_DATA: | |
343 | case ERROR_SUCCESS: | |
344 | break; | |
345 | ||
346 | default: | |
a1e19903 CV |
347 | print_win_error (rc); |
348 | return 1; | |
1fd5e000 CF |
349 | } |
350 | ||
351 | for (i = 0; i < entriesread; i++) | |
352 | { | |
375a780e CV |
353 | WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1]; |
354 | DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1; | |
355 | char psid_buffer[MAX_SID_LEN]; | |
1fd5e000 | 356 | PSID psid = (PSID) psid_buffer; |
375a780e | 357 | DWORD sid_length = MAX_SID_LEN; |
1fd5e000 CF |
358 | DWORD gid; |
359 | SID_NAME_USE acc_type; | |
375a780e CV |
360 | PDBGSID pdsid; |
361 | BOOL is_builtin = FALSE; | |
1fd5e000 | 362 | |
375a780e CV |
363 | if (!LookupAccountNameW (servername, buffer[i].lgrpi0_name, psid, |
364 | &sid_length, domain_name, &domname_len, | |
365 | &acc_type)) | |
1fd5e000 | 366 | { |
2d1bfd52 | 367 | print_win_error (GetLastError ()); |
375a780e | 368 | fprintf (stderr, " (%ls)\n", buffer[i].lgrpi0_name); |
1fd5e000 CF |
369 | continue; |
370 | } | |
6510edf4 CF |
371 | else if (acc_type == SidTypeDomain) |
372 | { | |
373 | WCHAR domname[MAX_DOMAIN_NAME_LEN + GNLEN + 2]; | |
374 | ||
375 | wcscpy (domname, domain_name); | |
376 | wcscat (domname, L"\\"); | |
377 | wcscat (domname, buffer[i].lgrpi0_name); | |
378 | sid_length = MAX_SID_LEN; | |
379 | domname_len = MAX_DOMAIN_NAME_LEN + 1; | |
380 | if (!LookupAccountNameW (servername, domname, | |
381 | psid, &sid_length, | |
382 | domain_name, &domname_len, | |
383 | &acc_type)) | |
384 | { | |
2d1bfd52 | 385 | print_win_error (GetLastError ()); |
375a780e | 386 | fprintf(stderr, " (%ls)\n", domname); |
6510edf4 CF |
387 | continue; |
388 | } | |
389 | } | |
1fd5e000 | 390 | |
375a780e CV |
391 | /* Store all local SIDs with prefix "S-1-5-32-" and check if it |
392 | has been printed already. This allows to get all builtin | |
393 | groups exactly once and not once per domain. */ | |
394 | pdsid = (PDBGSID) psid; | |
395 | if (pdsid->IdentifierAuthority.Value[5] == sid_nt_auth.Value[5] | |
396 | && pdsid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID) | |
397 | { | |
398 | int b; | |
399 | ||
f9519bcd | 400 | if (!print_builtin) |
6510edf4 | 401 | goto skip_group; |
375a780e | 402 | is_builtin = TRUE; |
f9519bcd | 403 | if (builtin_sid_cnt) |
375a780e CV |
404 | for (b = 0; b < builtin_sid_cnt; b++) |
405 | if (EqualSid (&builtin_sid_list[b], psid)) | |
406 | goto skip_group; | |
407 | if (builtin_sid_cnt < MAX_BUILTIN_SIDS) | |
408 | CopySid (sizeof (DBGSID), &builtin_sid_list[builtin_sid_cnt++], | |
409 | psid); | |
410 | } | |
6510edf4 CF |
411 | if (!print_current) |
412 | /* fall through */; | |
413 | else if (EqualSid (curr_pgrp.psid, psid)) | |
9258eca9 | 414 | got_curr_pgrp = TRUE; |
328b090f | 415 | |
1fd5e000 | 416 | gid = *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1); |
61522196 | 417 | printf ("%ls%s%ls:%s:%" PRIu32 ":\n", |
f9519bcd CV |
418 | with_dom && !is_builtin ? domain_name : L"", |
419 | with_dom && !is_builtin ? sep : "", | |
a1e19903 CV |
420 | buffer[i].lgrpi0_name, |
421 | put_sid (psid), | |
61522196 | 422 | (unsigned int) (gid + (is_builtin ? 0 : id_offset))); |
375a780e CV |
423 | skip_group: |
424 | ; | |
1fd5e000 CF |
425 | } |
426 | ||
375a780e | 427 | NetApiBufferFree (buffer); |
1fd5e000 CF |
428 | |
429 | } | |
0ac91154 | 430 | while (rc == ERROR_MORE_DATA); |
1fd5e000 | 431 | |
91dd009e | 432 | /* Return 1 if the single group we're looking for has been found here to |
5a625821 CV |
433 | avoid calling enum_groups for the same group, thus avoiding a spurious |
434 | error message "group name could not be found" in enum_groups. */ | |
91dd009e | 435 | return disp_groupname && entriesread ? 1 : 0; |
1fd5e000 CF |
436 | } |
437 | ||
6510edf4 | 438 | static void |
a1e19903 | 439 | enum_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep, |
6510edf4 | 440 | DWORD id_offset, char *disp_groupname, int print_current) |
1fd5e000 | 441 | { |
a1e19903 CV |
442 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; |
443 | PWCHAR servername = NULL; | |
444 | char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL; | |
445 | BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE; | |
1fd5e000 CF |
446 | GROUP_INFO_2 *buffer; |
447 | DWORD entriesread = 0; | |
448 | DWORD totalentries = 0; | |
61522196 | 449 | DWORD_PTR resume_handle = 0; |
a1e19903 | 450 | WCHAR gname[GNLEN + 1]; |
0ac91154 | 451 | DWORD rc; |
1fd5e000 | 452 | |
a1e19903 CV |
453 | if (domain) |
454 | { | |
455 | servername = get_dcname (d_or_m); | |
456 | if (servername == (PWCHAR) -1) | |
457 | return; | |
458 | } | |
459 | else if (d_or_m) | |
460 | { | |
461 | int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1); | |
462 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) | |
6510edf4 | 463 | { |
a1e19903 | 464 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", |
92b499ac | 465 | program_invocation_short_name, d_or_m); |
a1e19903 CV |
466 | return; |
467 | } | |
468 | servername = machine; | |
469 | } | |
470 | ||
1fd5e000 CF |
471 | do |
472 | { | |
473 | DWORD i; | |
1fd5e000 | 474 | |
e3118d88 CV |
475 | if (disp_groupname != NULL) |
476 | { | |
a1e19903 CV |
477 | mbstowcs (gname, disp_groupname, GNLEN + 1); |
478 | rc = NetGroupGetInfo (servername, (LPWSTR) & gname, 2, | |
375a780e | 479 | (void *) &buffer); |
e3118d88 | 480 | entriesread=1; |
91dd009e CV |
481 | /* Avoid annoying error messages just because the group hasn't been |
482 | found. */ | |
483 | if (rc == NERR_GroupNotFound) | |
484 | return; | |
e3118d88 | 485 | } |
6510edf4 | 486 | else |
375a780e CV |
487 | rc = NetGroupEnum (servername, 2, (void *) & buffer, |
488 | MAX_PREFERRED_LENGTH, &entriesread, &totalentries, | |
489 | &resume_handle); | |
1fd5e000 CF |
490 | switch (rc) |
491 | { | |
492 | case ERROR_ACCESS_DENIED: | |
a1e19903 CV |
493 | print_win_error (rc); |
494 | return; | |
1fd5e000 CF |
495 | |
496 | case ERROR_MORE_DATA: | |
497 | case ERROR_SUCCESS: | |
498 | break; | |
499 | ||
500 | default: | |
a1e19903 CV |
501 | print_win_error (rc); |
502 | return; | |
1fd5e000 CF |
503 | } |
504 | ||
505 | for (i = 0; i < entriesread; i++) | |
506 | { | |
375a780e CV |
507 | WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1]; |
508 | DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1; | |
509 | char psid_buffer[MAX_SID_LEN]; | |
1fd5e000 | 510 | PSID psid = (PSID) psid_buffer; |
375a780e | 511 | DWORD sid_length = MAX_SID_LEN; |
1fd5e000 CF |
512 | SID_NAME_USE acc_type; |
513 | ||
514 | int gid = buffer[i].grpi2_group_id; | |
a1e19903 CV |
515 | if (!LookupAccountNameW (servername, buffer[i].grpi2_name, |
516 | psid, &sid_length, | |
517 | domain_name, &domname_len, | |
518 | &acc_type)) | |
519 | { | |
2d1bfd52 | 520 | print_win_error (GetLastError ()); |
a1e19903 CV |
521 | fprintf(stderr, " (%ls)\n", buffer[i].grpi2_name); |
522 | continue; | |
523 | } | |
524 | else if (acc_type == SidTypeDomain) | |
525 | { | |
526 | WCHAR domname[MAX_DOMAIN_NAME_LEN + GNLEN + 2]; | |
527 | ||
7e1fdc9e CV |
528 | wcscpy (domname, domain || !servername |
529 | ? domain_name : servername); | |
a1e19903 CV |
530 | wcscat (domname, L"\\"); |
531 | wcscat (domname, buffer[i].grpi2_name); | |
532 | sid_length = MAX_SID_LEN; | |
533 | domname_len = MAX_DOMAIN_NAME_LEN + 1; | |
534 | if (!LookupAccountNameW (servername, domname, | |
535 | psid, &sid_length, | |
536 | domain_name, &domname_len, | |
537 | &acc_type)) | |
538 | { | |
2d1bfd52 | 539 | print_win_error (GetLastError ()); |
a1e19903 CV |
540 | fprintf(stderr, " (%ls)\n", domname); |
541 | continue; | |
542 | } | |
543 | } | |
6510edf4 CF |
544 | if (!print_current) |
545 | /* fall through */; | |
546 | else if (EqualSid (curr_pgrp.psid, psid)) | |
9258eca9 | 547 | got_curr_pgrp = TRUE; |
328b090f | 548 | |
61522196 | 549 | printf ("%ls%s%ls:%s:%" PRIu32 ":\n", |
a1e19903 CV |
550 | with_dom ? domain_name : L"", |
551 | with_dom ? sep : "", | |
552 | buffer[i].grpi2_name, | |
553 | put_sid (psid), | |
61522196 | 554 | (unsigned int) (id_offset + gid)); |
1fd5e000 CF |
555 | } |
556 | ||
375a780e | 557 | NetApiBufferFree (buffer); |
1fd5e000 CF |
558 | |
559 | } | |
0ac91154 | 560 | while (rc == ERROR_MORE_DATA); |
1fd5e000 CF |
561 | } |
562 | ||
6510edf4 | 563 | static void |
036fb477 CV |
564 | print_special_by_sid (PSID_IDENTIFIER_AUTHORITY auth, BYTE cnt, |
565 | DWORD sub1, DWORD sub2, DWORD sub3, DWORD sub4, | |
566 | DWORD sub5, DWORD sub6, DWORD sub7, DWORD sub8) | |
011ec894 | 567 | { |
0bdab5c8 CV |
568 | WCHAR grp[GNLEN + 1], dom[MAX_DOMAIN_NAME_LEN + 1]; |
569 | DWORD glen, dlen, rid; | |
570 | PSID psid; | |
571 | SID_NAME_USE acc_type; | |
011ec894 CV |
572 | |
573 | if (AllocateAndInitializeSid (auth, cnt, sub1, sub2, sub3, sub4, | |
6510edf4 | 574 | sub5, sub6, sub7, sub8, &psid)) |
011ec894 | 575 | { |
0bdab5c8 CV |
576 | if (LookupAccountSidW (NULL, psid, |
577 | grp, (glen = GNLEN + 1, &glen), | |
578 | dom, (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen), | |
579 | &acc_type)) | |
011ec894 CV |
580 | { |
581 | if (sub8) | |
582 | rid = sub8; | |
583 | else if (sub7) | |
584 | rid = sub7; | |
585 | else if (sub6) | |
586 | rid = sub6; | |
587 | else if (sub5) | |
588 | rid = sub5; | |
589 | else if (sub4) | |
590 | rid = sub4; | |
591 | else if (sub3) | |
592 | rid = sub3; | |
593 | else if (sub2) | |
594 | rid = sub2; | |
595 | else | |
596 | rid = sub1; | |
61522196 CV |
597 | printf ("%ls:%s:%" PRIu32 ":\n", grp, put_sid (psid), |
598 | (unsigned int) rid); | |
6510edf4 | 599 | } |
0bdab5c8 | 600 | FreeSid (psid); |
011ec894 CV |
601 | } |
602 | } | |
603 | ||
036fb477 CV |
604 | static void |
605 | print_special_by_name (PCWSTR name, gid_t gid) | |
606 | { | |
607 | DWORD size = 256, dom_size = 256; | |
608 | PSID sid = (PSID) alloca (size); | |
609 | WCHAR dom[dom_size]; | |
610 | SID_NAME_USE use; | |
611 | ||
612 | PWCHAR name_only = wcschr (name, L'\\'); | |
613 | if (name_only) | |
614 | ++name_only; | |
615 | ||
616 | if (LookupAccountNameW (NULL, name, sid, &size, dom, &dom_size, &use)) | |
617 | printf ("%ls:%s:%lu:\n", | |
618 | name_only ?: name, put_sid (sid), (unsigned long) gid); | |
619 | } | |
620 | ||
6510edf4 | 621 | static int |
a1e19903 | 622 | usage (FILE * stream) |
1fd5e000 | 623 | { |
a1e19903 | 624 | fprintf (stream, |
92b499ac CV |
625 | "Usage: %s [OPTION]...\n" |
626 | "\n" | |
a1e19903 CV |
627 | "Print /etc/group file to stdout\n" |
628 | "\n" | |
629 | "Options:\n" | |
92b499ac | 630 | "\n" |
f9519bcd CV |
631 | " -l,--local [machine[,offset]]\n" |
632 | " print local groups with gid offset offset\n" | |
633 | " (from local machine if no machine specified)\n" | |
634 | " -L,--Local [machine[,offset]]\n" | |
635 | " ditto, but generate groupname with machine prefix\n" | |
636 | " -d,--domain [domain[,offset]]\n" | |
637 | " print domain groups with gid offset offset\n" | |
638 | " (from current domain if no domain specified)\n" | |
639 | " -D,--Domain [domain[,offset]]\n" | |
640 | " ditto, but generate groupname with machine prefix\n" | |
a1e19903 CV |
641 | " -c,--current print current group\n" |
642 | " -C,--Current ditto, but generate groupname with machine or\n" | |
643 | " domain prefix\n" | |
644 | " -S,--separator char for -L, -D, -C use character char as domain\\group\n" | |
645 | " separator in groupname instead of the default '\\'\n" | |
646 | " -o,--id-offset offset change the default offset (10000) added to gids\n" | |
647 | " in domain or foreign server accounts.\n" | |
648 | " -g,--group groupname only return information for the specified group\n" | |
649 | " one of -l, -L, -d, -D must be specified, too\n" | |
f9519bcd | 650 | " -b,--no-builtin don't print BUILTIN groups\n" |
0bdab5c8 CV |
651 | " -U,--unix grouplist additionally print UNIX groups when using -l or -L\n" |
652 | " on a UNIX Samba server\n" | |
653 | " grouplist is a comma-separated list of groupnames\n" | |
654 | " or gid ranges (root,-25,50-100).\n" | |
655 | " (enumerating large ranges can take a long time!)\n" | |
a1e19903 CV |
656 | " -s,--no-sids (ignored)\n" |
657 | " -u,--users (ignored)\n" | |
658 | " -h,--help print this message\n" | |
659 | " -v,--version print version information and exit\n" | |
660 | "\n" | |
661 | "Default is to print local groups on stand-alone machines, plus domain\n" | |
92b499ac CV |
662 | "groups on domain controllers and domain member machines.\n" |
663 | "\n", program_invocation_short_name); | |
f1c9046a | 664 | return 1; |
1fd5e000 CF |
665 | } |
666 | ||
315f8fd3 | 667 | struct option longopts[] = { |
f9519bcd | 668 | {"no-builtin", no_argument, NULL, 'b'}, |
f1c9046a | 669 | {"current", no_argument, NULL, 'c'}, |
a1e19903 CV |
670 | {"Current", no_argument, NULL, 'C'}, |
671 | {"domain", optional_argument, NULL, 'd'}, | |
672 | {"Domain", optional_argument, NULL, 'D'}, | |
673 | {"group", required_argument, NULL, 'g'}, | |
674 | {"help", no_argument, NULL, 'h'}, | |
675 | {"local", optional_argument, NULL, 'l'}, | |
676 | {"Local", optional_argument, NULL, 'L'}, | |
9f2cad57 | 677 | {"id-offset", required_argument, NULL, 'o'}, |
315f8fd3 | 678 | {"no-sids", no_argument, NULL, 's'}, |
a1e19903 | 679 | {"separator", required_argument, NULL, 'S'}, |
8a3adc99 | 680 | {"users", no_argument, NULL, 'u'}, |
0bdab5c8 | 681 | {"unix", required_argument, NULL, 'U'}, |
92b499ac | 682 | {"version", no_argument, NULL, 'V'}, |
315f8fd3 CV |
683 | {0, no_argument, NULL, 0} |
684 | }; | |
685 | ||
92b499ac | 686 | static char opts[] = "bcCd::D::g:hl::L::o:sS:uU:V"; |
22a9157c | 687 | |
6510edf4 | 688 | static void |
22a9157c CV |
689 | print_version () |
690 | { | |
92b499ac | 691 | printf ("mkgroup (cygwin) %d.%d.%d\n" |
1b23b30b CF |
692 | "Group File Generator\n" |
693 | "Copyright (C) 1997 - %s Red Hat, Inc.\n" | |
694 | "This is free software; see the source for copying conditions. There is NO\n" | |
92b499ac | 695 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", |
1b23b30b CF |
696 | CYGWIN_VERSION_DLL_MAJOR / 1000, |
697 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
698 | CYGWIN_VERSION_DLL_MINOR, | |
699 | strrchr (__DATE__, ' ') + 1); | |
22a9157c | 700 | } |
315f8fd3 | 701 | |
a1e19903 CV |
702 | static PPOLICY_PRIMARY_DOMAIN_INFO p_dom; |
703 | ||
704 | static BOOL | |
705 | fetch_primary_domain () | |
706 | { | |
707 | NTSTATUS status; | |
708 | LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 }; | |
709 | LSA_HANDLE lsa; | |
710 | ||
711 | if (!p_dom) | |
712 | { | |
713 | status = LsaOpenPolicy (NULL, &oa, POLICY_EXECUTE, &lsa); | |
714 | if (!NT_SUCCESS (status)) | |
715 | return FALSE; | |
716 | status = LsaQueryInformationPolicy (lsa, PolicyPrimaryDomainInformation, | |
6510edf4 | 717 | (PVOID *) ((void *) &p_dom)); |
a1e19903 CV |
718 | LsaClose (lsa); |
719 | if (!NT_SUCCESS (status)) | |
720 | return FALSE; | |
721 | } | |
722 | return !!p_dom->Sid; | |
723 | } | |
724 | ||
1fd5e000 CF |
725 | int |
726 | main (int argc, char **argv) | |
727 | { | |
f9519bcd CV |
728 | int print_domlist = 0; |
729 | domlist_t domlist[32]; | |
730 | char *opt, *p, *ep; | |
a1e19903 | 731 | int print_current = 0; |
4aeb44ee | 732 | int print_system = 0; |
f9519bcd | 733 | int print_builtin = 1; |
0bdab5c8 | 734 | char *print_unix = NULL; |
a1e19903 | 735 | const char *sep_char = "\\"; |
f9519bcd CV |
736 | DWORD id_offset = 10000, off; |
737 | int c, i; | |
e3118d88 | 738 | char *disp_groupname = NULL; |
a1e19903 | 739 | BOOL in_domain; |
01dd3162 | 740 | int optional_args = 0; |
eeec2a48 | 741 | |
a1e19903 CV |
742 | if (!isatty (1)) |
743 | setmode (1, O_BINARY); | |
f1c9046a | 744 | |
73535010 CV |
745 | /* Use locale from environment. If not set or set to "C", use UTF-8. */ |
746 | setlocale (LC_CTYPE, ""); | |
747 | if (!strcmp (setlocale (LC_CTYPE, NULL), "C")) | |
748 | setlocale (LC_CTYPE, "en_US.UTF-8"); | |
a1e19903 | 749 | in_domain = fetch_primary_domain (); |
6510edf4 CF |
750 | fetch_current_pgrp_sid (); |
751 | ||
a1e19903 | 752 | if (argc == 1) |
011ec894 | 753 | { |
036fb477 CV |
754 | print_special_by_sid (&sid_nt_auth, 1, SECURITY_LOCAL_SYSTEM_RID, |
755 | 0, 0, 0, 0, 0, 0, 0); | |
a1e19903 | 756 | if (in_domain) |
011ec894 | 757 | { |
a1e19903 | 758 | if (!enum_local_groups (TRUE, NULL, sep_char, id_offset, |
6510edf4 CF |
759 | disp_groupname, print_builtin, 0)) |
760 | enum_groups (TRUE, NULL, sep_char, id_offset, disp_groupname, 0); | |
011ec894 | 761 | } |
f9519bcd | 762 | else if (!enum_local_groups (FALSE, NULL, sep_char, 0, disp_groupname, |
6510edf4 CF |
763 | print_builtin, 0)) |
764 | enum_groups (FALSE, NULL, sep_char, 0, disp_groupname, 0); | |
315f8fd3 CV |
765 | return 0; |
766 | } | |
f1c9046a | 767 | |
01dd3162 | 768 | unsetenv ("POSIXLY_CORRECT"); /* To get optional arg processing right. */ |
a1e19903 CV |
769 | while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) |
770 | switch (c) | |
771 | { | |
f9519bcd CV |
772 | case 'd': |
773 | case 'D': | |
a1e19903 CV |
774 | case 'l': |
775 | case 'L': | |
f9519bcd | 776 | if (print_domlist >= 32) |
a1e19903 | 777 | { |
f9519bcd | 778 | fprintf (stderr, "%s: Can not enumerate from more than 32 " |
92b499ac CV |
779 | "domains and machines.\n", |
780 | program_invocation_short_name); | |
a1e19903 CV |
781 | return 1; |
782 | } | |
f36c78a4 | 783 | domlist[print_domlist].domain = (c == 'd' || c == 'D'); |
a1e19903 CV |
784 | opt = optarg ?: |
785 | argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL; | |
096df177 | 786 | if (argv[optind] && opt == argv[optind]) |
01dd3162 | 787 | ++optional_args; |
f9519bcd | 788 | for (i = 0; i < print_domlist; ++i) |
f36c78a4 CV |
789 | if (domlist[i].domain == domlist[print_domlist].domain |
790 | && ((!domlist[i].str && !opt) | |
791 | || (domlist[i].str && opt | |
792 | && (off = strlen (domlist[i].str)) | |
793 | && !strncmp (domlist[i].str, opt, off) | |
794 | && (!opt[off] || opt[off] == ',')))) | |
795 | { | |
796 | fprintf (stderr, "%s: Duplicate %s '%s'. Skipping...\n", | |
92b499ac CV |
797 | program_invocation_short_name, |
798 | domlist[i].domain ? "domain" : "machine", | |
f36c78a4 CV |
799 | domlist[i].str); |
800 | goto skip; | |
801 | } | |
f9519bcd | 802 | if (!(domlist[print_domlist].str = opt)) |
4aeb44ee | 803 | print_system = 1; |
61522196 | 804 | domlist[print_domlist].id_offset = UINT32_MAX; |
f9519bcd | 805 | if (opt && (p = strchr (opt, ','))) |
a1e19903 | 806 | { |
f9519bcd | 807 | if (p == opt |
05e6f7b2 | 808 | || !isdigit ((unsigned char) p[1]) |
6510edf4 | 809 | || (domlist[print_domlist].id_offset = strtol (p + 1, &ep, 10) |
f9519bcd CV |
810 | , *ep)) |
811 | { | |
812 | fprintf (stderr, "%s: Malformed machine,offset string '%s'. " | |
92b499ac | 813 | "Skipping...\n", program_invocation_short_name, opt); |
6510edf4 | 814 | break; |
f9519bcd CV |
815 | } |
816 | *p = '\0'; | |
a1e19903 | 817 | } |
f9519bcd | 818 | domlist[print_domlist++].with_dom = (c == 'D' || c == 'L'); |
f36c78a4 | 819 | skip: |
a1e19903 CV |
820 | break; |
821 | case 'S': | |
822 | sep_char = optarg; | |
823 | if (strlen (sep_char) > 1) | |
824 | { | |
825 | fprintf (stderr, "%s: Only one character allowed as domain\\user " | |
92b499ac CV |
826 | "separator character.\n", |
827 | program_invocation_short_name); | |
a1e19903 CV |
828 | return 1; |
829 | } | |
830 | if (*sep_char == ':') | |
831 | { | |
832 | fprintf (stderr, "%s: Colon not allowed as domain\\user separator " | |
92b499ac | 833 | "character.\n", program_invocation_short_name); |
a1e19903 CV |
834 | return 1; |
835 | } | |
6510edf4 | 836 | break; |
0bdab5c8 | 837 | case 'U': |
6510edf4 | 838 | print_unix = optarg; |
0bdab5c8 | 839 | break; |
a1e19903 | 840 | case 'c': |
6510edf4 | 841 | sep_char = NULL; |
a1e19903 CV |
842 | /*FALLTHRU*/ |
843 | case 'C': | |
844 | print_current = 1; | |
845 | break; | |
846 | case 'o': | |
847 | id_offset = strtol (optarg, NULL, 10); | |
848 | break; | |
f9519bcd | 849 | case 'b': |
6510edf4 | 850 | print_builtin = 0; |
f9519bcd | 851 | break; |
a1e19903 CV |
852 | case 's': |
853 | break; | |
854 | case 'u': | |
855 | break; | |
856 | case 'g': | |
857 | disp_groupname = optarg; | |
a1e19903 CV |
858 | break; |
859 | case 'h': | |
860 | usage (stdout); | |
861 | return 0; | |
92b499ac | 862 | case 'V': |
a1e19903 CV |
863 | print_version (); |
864 | return 0; | |
865 | default: | |
92b499ac | 866 | fprintf (stderr, "Try `%s --help' for more information.\n", argv[0]); |
a1e19903 CV |
867 | return 1; |
868 | } | |
1fd5e000 | 869 | |
01dd3162 | 870 | optind += optional_args; |
6510edf4 CF |
871 | if (argv[optind]) |
872 | { | |
873 | fprintf (stderr, | |
874 | "mkgroup: non-option command line argument `%s' is not allowed.\n" | |
875 | "Try `mkgroup --help' for more information.\n", argv[optind]); | |
876 | exit (1); | |
877 | } | |
878 | ||
a1e19903 | 879 | /* Get 'system' group */ |
328b090f | 880 | if (!disp_groupname && print_system && print_builtin && print_domlist) |
036fb477 CV |
881 | { |
882 | print_special_by_sid (&sid_nt_auth, 1, SECURITY_LOCAL_SYSTEM_RID, | |
883 | 0, 0, 0, 0, 0, 0, 0); | |
884 | print_special_by_name (L"NT SERVICE\\TrustedInstaller", -2); | |
885 | } | |
1fd5e000 | 886 | |
f9519bcd CV |
887 | off = id_offset; |
888 | for (i = 0; i < print_domlist; ++i) | |
a1e19903 | 889 | { |
f9519bcd | 890 | DWORD my_off = (domlist[i].domain || domlist[i].str) |
61522196 | 891 | ? domlist[i].id_offset != UINT_MAX |
f9519bcd CV |
892 | ? domlist[i].id_offset : off : 0; |
893 | if (!enum_local_groups (domlist[i].domain, domlist + i, sep_char, | |
6510edf4 | 894 | my_off, disp_groupname, print_builtin, print_current)) |
4aeb44ee | 895 | { |
f9519bcd CV |
896 | if (!domlist[i].domain && domlist[i].str && print_unix) |
897 | enum_unix_groups (domlist + i, sep_char, my_off, print_unix); | |
898 | enum_groups (domlist[i].domain, domlist + i, sep_char, my_off, | |
6510edf4 | 899 | disp_groupname, print_current); |
f9519bcd CV |
900 | if (my_off) |
901 | off += id_offset; | |
4aeb44ee | 902 | } |
a1e19903 CV |
903 | } |
904 | ||
9258eca9 | 905 | if (print_current && !got_curr_pgrp) |
f9519bcd | 906 | current_group (sep_char, off); |
f1c9046a | 907 | |
1fd5e000 CF |
908 | return 0; |
909 | } |